def error(message, jump_url='.', msg_title="error", status=0, wait_second=3):
    json_data = dict()
    json_data['msg_title'] = msg_title
    json_data['info'] = str(message)
    json_data['url'] = jump_url
    json_data['status'] = status
    json_data['wait_second'] = wait_second
    return JsonResponse(json_data) def success(message, jump_url='.', msg_title="success", status=1, wait_second=1):
    json_data = dict()
    json_data['msg_title'] = msg_title
    json_data['info'] = message
    json_data['url'] = jump_url
    json_data['status'] = status
    json_data['wait_second'] = wait_second
    return JsonResponse(json_data) def delete_file(file_path):
    import os
    try:
        if os.path.exists(file_path):
            os.remove(file_path)
            return success('Deleted')
        else:
            return error('File can not be found.')
    except Exception as e:
        return error(e) def os_to_int():
    import platform
    if platform.system() == 'Linux':
        return 1
    elif platform.system() == 'Darwin':
        return 3
    else:
        return 2 def page_info(page_model, page):
    try:
        items = page_model.page(page)
    except PageNotAnInteger:
        items = page_model.page(1)
    except EmptyPage:
        items = page_model.page(page_model.num_pages)
    return items def get_config(section, key, const=0):
    """
    Get a configuration
    :param section: string, the config insides which section
    :param key: string
    :param const: int, 0 means to look up user's conf, 1 means to look up bioqueue's conf
    :return:
    """
    try:
        config = config_init(const)
        return config.get(section, key)
    except:
        return None def get_disk_free(path='/'):
    du = psutil.disk_usage(path)
    return list(du)[2] def get_disk_used(path='/'):
    du = psutil.disk_usage(path)
    return list(du)[1] def set_config(section, key, value, const=0):
    """
    Write config
    :param section: string, the config insides which section
    :param key: string
    :param value: string
    :param const: int, 0 means to write into user's conf, 1 means to write into bioqueue's conf
    :return:
    """
    config = config_init(const)
    config.set(section, key, value)
    if const:
        file_path = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] + '/config/bioqueue.conf'
    else:
        file_path = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] + '/config/custom.conf'

    config.write(open(file_path, "w")) def get_bioqueue_version():
    """
    Get BioQueue Version
    :return: string, version
    """
    import os
    version = ''
    version_file = os.path.split(os.path.split(os.path.realpath(__file__))[0])[0] + '/version'
    with open(version_file) as version_file_handle:
        version = version_file_handle.read()
    return version class Job(_OwnerModel):
    """Queue Table
    Save tasks
    """
    protocol = models.ForeignKey("ProtocolList", on_delete=models.PROTECT)
    protocol_ver = models.CharField(max_length=100, blank=True, null=True)
    job_name = models.CharField(max_length=100, blank=True, null=True)
    input_file = models.TextField()
    parameter = models.TextField()
    run_dir = models.TextField(blank=True, null=True)
    result = models.TextField(blank=True, null=True)
    status = models.SmallIntegerField(default=_JS_WAITING, choices=JOB_STATUS)
    update_time = models.DateTimeField(auto_now=True)
    create_time = models.DateTimeField(auto_now_add=True)
    resume = models.SmallIntegerField(default=0)
    ter = models.SmallIntegerField(default=_NO, choices=YES_OR_NO)
    audit = models.SmallIntegerField(default=_NO, choices=((0, "Ok"),
                                                           (1, "Protocol/Reference changed"),
                                                           (2, "Input/Output changed")))
    wait_for = models.SmallIntegerField(default=0, choices=CHECKPOINT_CHOICES)
    workspace = models.ForeignKey("Workspace", blank=True, null=True, on_delete=models.PROTECT)
    locked = models.SmallIntegerField(choices=((0, "Locked"), (1, "Not locked")), default=0)

    def __str__(self):
        return str(self.id)

    def terminate_job(self):
        self.ter = 1
        self.save()
        Audition(operation="Terminated",
                 related_job=self,
                 job_name=self.job_name,
                 prev_par=self.parameter,
                 new_par=self.parameter,
                 prev_input=self.input_file,
                 current_input=self.input_file,
                 protocol=self.protocol.name,
                 protocol_ver=self.protocol_ver,
                 resume_point=self.resume,
                 user=self.user).save()

    def rerun_job(self):
        if not self.locked:
            self.status = 0
            self.resume = 0
            self.ter = 0
            self.protocol_ver = self.protocol.ver
            self.audit = 0
            self.save()
            Audition(operation="Reran",
                    related_job=self,
                    job_name=self.job_name,
                    prev_par=self.parameter,
                    new_par=self.parameter,
                    prev_input=self.input_file,
                    current_input=self.input_file,
                    protocol=self.protocol.name,
                    protocol_ver=self.protocol_ver,
                    resume_point=self.resume,
                    user=self.user).save()

    def resume_job(self, rollback):
        if not self.locked:
            self.resume = rollback
            self.status = 0
            self.protocol_ver = self.protocol.ver
            self.audit = 0
            self.save()
            Audition(operation="Resumed",
                    related_job=self,
                    job_name=self.job_name,
                    prev_par=self.parameter,
                    new_par=self.parameter,
                    prev_input=self.input_file,
                    current_input=self.input_file,
                    protocol=self.protocol.name,
                    protocol_ver=self.protocol_ver,
                    resume_point=self.resume,
                    user=self.user).save()

    def set_done(self):
        if not self.locked:
            self.status = _JS_FINISHED
            self.save()
            Audition(operation="Done",
                    related_job=self,
                    job_name=self.job_name,
                    prev_par=self.parameter,
                    new_par=self.parameter,
                    prev_input=self.input_file,
                    current_input=self.input_file,
                    protocol=self.protocol.name,
                    protocol_ver=self.protocol_ver,
                    resume_point=self.resume,
                    user=self.user).save()
            if self.user.queuedb_profile_related.notification_enabled:
                Notification(msg=self.job_name+" ("+str(self.id)+") "+"is done.", user=self.user).save()

    def set_result(self, value):
        if not self.locked:
            self.result = value
            self.save()

    def set_status(self, status):
        if not self.locked:
            self.status = status
            self.save()

    def set_wait(self, for_what):
        if not self.locked:
            self.status = _JS_WAITING
            self.wait_for = for_what
            self.save()

    def get_result(self):
        return self.result

    def update_status(self, status):
        if not self.locked:
            self.status = status
            self.save()

    def update_inputs(self, new_inputs):
        if not self.locked:
            Audition(operation="Changed inputs",
                    related_job=self,
                    job_name=self.job_name,
                    prev_par=self.parameter,
                    new_par=self.parameter,
                    prev_input=self.input_file,
                    current_input=new_inputs,
                    protocol=self.protocol.name,
                    protocol_ver=self.protocol_ver,
                    resume_point=self.resume,
                    user=self.user).save()
            self.input_file = new_inputs
            self.audit = 1
            self.save()

    def update_parameter(self, new_par):
        if not self.locked:
            Audition(operation="Changed parameters",
                    related_job=self,
                    job_name=self.job_name,
                    prev_par=self.parameter,
                    new_par=new_par,
                    prev_input=self.input_file,
                    current_input=self.input_file,
                    protocol=self.protocol.name,
                    protocol_ver=self.protocol_ver,
                    resume_point=self.resume,
                    user=self.user).save()
            self.parameter = new_par
            self.audit = 1
            self.save() class ProtocolList(_OwnerModel):
    """Protocol List Table
    Save protocol names
    """
    name = models.CharField(max_length=500)
    description = models.TextField(null=True)
    ver = models.CharField(max_length=33, null=True)

    def __str__(self):
        return self.name class Step(_OwnerModel):
    """Protocol Table
    Save steps for protocol
    """
    software = models.CharField(max_length=50)
    parameter = models.TextField()
    specify_output = models.CharField(max_length=50, blank=True, null=True)
    parent = models.ForeignKey("ProtocolList", on_delete=models.CASCADE)
    hash = models.CharField(max_length=50)
    step_order = models.SmallIntegerField(default=1)
    env = models.ForeignKey("VirtualEnvironment", blank=True, null=True, on_delete=models.PROTECT)
    force_local = models.SmallIntegerField(default=0, choices=YES_OR_NO)
    cpu_prior = models.IntegerField(default=-1, blank=True)
    mem_prior = models.IntegerField(default=-1, blank=True)
    disk_prior = models.IntegerField(default=-1, blank=True)

    def __str__(self):
        return self.software + ' ' + self.parameter

    def check_parent(self, parent):
        if int(self.parent) == parent:
            return 1
        else:
            return 0

    def update_order(self, new_order):
        self.step_order = new_order
        return 1

    def update_parameter(self, new_parameter):
        import hashlib
        m = hashlib.md5(str(self.software + ' ' + new_parameter.strip()).encode())
        self.hash = m.hexdigest()
        self.parameter = new_parameter
        return 1 class Prediction(models.Model):
    """Prediction Table
    Save linear model for memory, output size
    Or average, sd for CPU
    """
    step_hash = models.CharField(max_length=50, db_index=True)
    a = models.CharField(max_length=50, blank=True, null=True)
    b = models.CharField(max_length=50, blank=True, null=True)
    r = models.CharField(max_length=50, blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)
    type = models.SmallIntegerField(choices=PREDICTION_CHOICES)

    def __str__(self):
        return self.step_hash

    def step_name(self):
        """
        step = Protocol.objects.filter(hash=self.step_hash)
        return step[0].software+' '+step[0].parameter
        """
        steps = Step.objects.filter(hash=self.step_hash)
        step_key = None
        if steps:
            step_key = steps[0]
        if step_key:
            return step_key.software + ' ' + step_key.parameter
        else:
            return ''

    step_name.admin_order_field = 'step_hash' class Reference(_OwnerModel):
    """Reference Table
    Save custom references
    """
    name = models.CharField(max_length=255)
    path = models.CharField(max_length=500)
    description = models.TextField()

    def __str__(self):
        return self.name class VirtualEnvironment(_OwnerModel):
    """Virtual Environment Table
    Save VEs which can be activated by source command
    """
    name = models.CharField(max_length=50)
    ve_type = models.CharField(choices=(("conda", "conda"),
                                        ("venv", "venv")),
                               max_length=10, default="conda")
    value = models.TextField()
    activation_command = models.TextField(null=True, blank=True)

    class Meta:
        try:
            constraints = [models.UniqueConstraint(fields=["name", "user"], name="unique_name")]
        except:
            unique_together = ("name", "user")

    def __str__(self):
        return self.name class Experiment(models.Model):
    name = models.CharField(max_length=300)
    required_fields = models.TextField()
    file_support = models.CharField(null=True, blank=True, max_length=500)

    def __str__(self):
        return self.name class Sample(_OwnerModel):
    name = models.CharField(max_length=500)
    file_path = models.TextField()  # real file path with uploads as prefix
    inner_path = models.TextField()  # file path without uploads as prefix
    experiment = models.ForeignKey(Experiment, on_delete=models.PROTECT, related_name="%(app_label)s_%(class)s_related")
    attribute = models.TextField(blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name class FileArchive(_OwnerModel):
    protocol = models.ForeignKey('ProtocolList', on_delete=models.PROTECT,
                                 related_name="%(app_label)s_%(class)s_related")
    protocol_ver = models.CharField(max_length=33, null=True)
    inputs = models.TextField()
    files = models.TextField()
    file_md5s = models.TextField()
    job = models.ForeignKey(Job, on_delete=models.PROTECT, null=True, blank=True)
    raw_files = models.TextField(null=True, blank=True)
    audit = models.SmallIntegerField(default=0)
    description = models.TextField(null=True, blank=True)
    comment = models.TextField(null=True, blank=True)
    status = models.SmallIntegerField(default=0)
    archive_file = models.TextField(null=True, blank=True)
    create_time = models.DateTimeField(auto_now_add=True)
    shared_with = models.TextField(null=True, blank=True)
    file_id_remote = models.TextField(null=True, blank=True, default="ph") class Workspace(_OwnerModel):
    name = models.CharField(max_length=255)
    description = models.TextField(null=True, blank=True)
    create_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return "%s - %s" % (self.name, self.user.username) def query_protocol_atom(is_staff, user, page):
    if is_staff:
        protocol_list = ProtocolList.objects.order_by('-id').all()
    else:
        protocol_list = ProtocolList.objects.filter(Q(user=user) | Q(user=None)).order_by('-id').all()

    paginator = Paginator(protocol_list, 25)
    return page_info(paginator, page)
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.admin.views.decorators import staff_member_required
from django.core.paginator import Paginator
from django.urls import reverse
from django.shortcuts import render, redirect
from django.template import loader
from django.http import HttpResponseRedirect, FileResponse, HttpResponse
from django.db.models import Q
from ..tools import error, success, delete_file, os_to_int, page_info
from worker.bases import get_config, get_disk_free, get_disk_used, set_config, get_bioqueue_version
from ..forms import *
from QueueDB.models import Job, ProtocolList, Step, Prediction, Reference, VirtualEnvironment, Experiment, Sample, \
    FileArchive, Workspace  #, GoogleDriveConnection
from urllib.parse import unquote
from urllib.request import urlopen, Request
from .Protocol import query_protocol_atom
            from ..tools import get_maintenance_protocols
        from ..tools import get_maintenance_protocols
        from ..ena import query_download_link_from_ebi
        from worker.cluster_support import get_cluster_models
import os
import re
import base64
    import json
#!/usr/bin/env python
# coding=utf-8
# Created by: Li Yao
# Created on: 5/25/20


class SharedSimpleViews:
    error_flag = 0
    supported_models = ("Experiment", "Sample", "Environment", "Workspace", "Reference", "Profile")
    internal_names = ("Experiment", "Sample", "VirtualEnvironment", "Workspace", "Reference", "Profile")
    models = (Experiment, Sample, VirtualEnvironment, Workspace, Reference, Profile)
    create_forms = (None, None, CreateVEForm, CreateWorkspaceForm, CreateReferenceForm, None)
    update_forms = (None, None, CreateVEForm, CreateWorkspaceForm, CreateReferenceForm, UpdateUserFoldersForm)
    update_action = ("redirect", "redirect", "redirect", "redirect", "redirect", "msg", )
    helper_msgs = (
        "",
        "",
        """BioQueue supports running steps in environments created by <a href="https://docs.python.org/3/library/venv.html" target="_blank"><code>venv</code></a> and <a href="https://docs.conda.io/en/latest/" target="_blank"><code>conda</code></a>. You can configure environments for steps to use in this page.""",
        "BioQueue supports putting jobs into different workspaces to make them more manageable.",
        "Specific files are likely to be used in multiple protocols (like gene annotation files, genome sequences files). To avoid the cumbersome typing of the entire path repeatedly, BioQueue allows users to create a dictionary of aliases (references) of these files so that you can refer to them with ease. ",
        ""
    )

    def __init__(self, request, model):
        if model not in self.supported_models:
            self.error_flag = 1
        if not self.error_flag:
            self.request = request
            self.model_name = model
            self.order_i = self.supported_models.index(self.model_name)
            self.model = self.models[self.order_i]

    def create(self):
        if self.request.method == "POST":
            form = self.create_forms[self.order_i](self.request.POST, request=self.request)
            if form.is_valid():
                if self.request.user.has_perm("QueueDB.add_%s" % self.internal_names[self.order_i].lower()):
                    td = form.save(commit=False)
                    if self.request.user.is_staff:
                        u = None
                    else:
                        u = self.request.user.queuedb_profile_related.delegate
                    td.user = u
                    td.save()
                    return success("%s created successfully." % self.model_name)
                else:
                    return success("Permission denied.")
            else:
                return error(form.errors)
        else:
            return render(self.request, "ui/create_simple_models.html",
                          {"form": self.create_forms[self.order_i](),
                           "model_name": self.model_name,
                           "page_help": self.helper_msgs[self.order_i],
                           "model_name_lower": self.model_name.lower()})

    def delete(self):
        try:
            form = GenericDeleteForm(self.request.GET)
            if form.is_valid():
                # make sure current user has the permission to delete records
                if self.request.user.has_perm("QueueDB.delete_%s" % self.internal_names[self.order_i].lower()):
                    try:
                        target = self.model.objects.get(id=form.cleaned_data["id"])

                        if target.check_owner(self.request.user.queuedb_profile_related.delegate, read_only=False):
                            target.delete()
                            return success("%s deleted successfully." % self.model_name)
                        else:
                            return error("You are not the owner of the record")
                    except Exception as e:
                        return error(e)
                else:

                    return error("Permission denied.")