move job submission to dedicated function
[qcg-portal.git] / qcg / utils.py
1 # coding=utf-8
2
3 from functools import wraps
4
5 import os
6 import string
7 import random
8
9 from django.contrib.auth.decorators import login_required
10 from django.core.paginator import Paginator
11 from django.utils.formats import date_format
12 from django.utils.timezone import localtime
13 from django.views.decorators.cache import cache_control
14 from pyqcg import QCG
15 from pyqcg.description import JobDescription
16
17 from filex.ftp import FTPOperation
18 from qcg import constants
19
20 from django.utils import encoding
21
22 def get_attributes(obj, attrs):
23     return {name: getattr(obj, name) for name in attrs if getattr(obj, name) is not None}
24
25
26 def username_from_dn(dn):
27     _, username = dn.rsplit('=', 1)
28
29     return username
30
31
32 def try_parse_int(s, default=None, base=10):
33     try:
34         return int(s, base)
35     except (TypeError, ValueError):
36         return default
37
38
39 def paginator_context(request, objects, per_page=constants.PER_PAGE):
40     paginator = Paginator(objects, per_page)
41
42     page_num = try_parse_int(request.GET.get('page'), 1)
43     if not (1 <= page_num <= paginator.num_pages):
44         page_num = 1
45
46     pages_range = range(max(2, min(page_num - 2, paginator.num_pages - 4)),
47                         min(max(page_num + 2, 5), paginator.num_pages) + 1)
48
49     return {'page': paginator.page(page_num), 'num_pages': paginator.num_pages, 'pages_range': pages_range}
50
51
52 def localtime_str(datetime):
53     return date_format(localtime(datetime), 'DATETIME_FORMAT')
54
55
56 def random_id(size=8, chars=string.ascii_uppercase + string.digits):
57     return ''.join(random.choice(chars) for _ in range(size))
58
59
60 def chunks(seq, size):
61     return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
62
63 def generate_md_inputfile(params):
64     md_input = list()
65     # Opis pliku wyjsciowego
66     opis=params['note'][:80]
67     md_input.append(encoding.smart_str(opis, encoding='ascii', errors='ignore'))
68     # Dane kontrolne obliczen
69     md_input.append('SEED=-3059743 PDBREF ONE_LETTER MD EXTCONF RESCALE_MODE=2')
70     ctl_data='nstep='+str(params['nstep'])+' ntwe='+str(params['ntwe'])
71     ctl_data+=' ntwx='+str(params['ntwx'])+' dt='+str(params['dt'])+' damax='+str(params['damax'])+'lang=0 tbf'
72     md_input.append('{:<79}&'.format(ctl_data))
73     md_input.append('tau_bath=1.0 t_bath=300 reset_vel=10000 respa ntime_split=1 maxtime_split=512')
74     # Paramatry pól siłowych
75     if params['force_field'] == 'GAB':
76         # Wagi pola GAB
77         md_input.append('WLONG=1.35279 WSCP=1.59304 WELEC=0.71534 WBOND=1.00000 WANG=1.13873            &')
78         md_input.append('WSCLOC=0.16258 WTOR=1.98599 WTORD=1.57069 WCORRH=0.42887 WCORR5=0.00000        &')
79         md_input.append('WCORR6=0.00000 WEL_LOC=0.16036 WTURN3=1.68722 WTURN4=0.66230 WTURN6=0.00000    &')
80         md_input.append('WVDWPP=0.11371 WHPB=1.00000                                                    &')
81         md_input.append('CUTOFF=7.00000 WCORR4=0.00000 WSCCOR=0.0')
82     else:
83         # Wagi pola E0LLY
84         md_input.append('WLONG=1.00000 WSCP=1.23315 WELEC=0.84476 WBOND=1.00000 WANG=0.62954            &')
85         md_input.append('WSCLOC=0.10554 WTOR=1.84316 WTORD=1.26571 WCORRH=0.19212 WCORR5=0.00000        &')
86         md_input.append('WCORR6=0.00000 WEL_LOC=0.37357 WTURN3=1.40323 WTURN4=0.64673 WTURN6=0.00000    &')
87         md_input.append('WVDWPP=0.23173 WHPB=1.00000 WSCCOR=0.0                                         &')
88         md_input.append('CUTOFF=7.00000 WCORR4=0.00000')
89     # Plik PDB    
90     md_input.append(params['pdb_file'].split('/')[-1])
91     # Sekwencja aminokwasów
92     md_input.append(len(params['sequence']))
93     seq_str=params['sequence']
94     while seq_str:
95         md_input.append(seq_str[:80])
96         seq_str=seq_str[80:]
97     md_input.append(' 0')
98     md_input.append(' 0')
99     
100     return md_input
101
102
103 def to_job_desc(params, proxy):
104     QCG.start()
105     desc = JobDescription()
106
107     direct_map = ('env_variables', 'executable', 'arguments', 'note', 'grant', 'hosts', 'properties', 'queue', 'procs',
108                   'wall_time', 'memory', 'memory_per_slot', 'modules', 'input', 'stage_in', 'native', 'notify',
109                   'preprocess', 'postprocess', 'persistent')
110
111     for name in direct_map:
112         if params[name]:
113             setattr(desc, name, params[name])
114
115     if params['application']:
116         desc.set_application(*params['application'])
117         desc.stage_in += [params['master_file']]
118         desc.arguments.insert(0, os.path.basename(params['master_file']))
119     if params['script']:
120         ftp = FTPOperation(proxy)
121
122         ftp.mkdir(constants.QCG_DATA_URL, parents=True)
123         url = os.path.join(constants.QCG_DATA_URL, 'script.{}.sh'.format(random_id()))
124         ftp.put(url)
125
126         for chunk in chunks(params['script'], 4096):
127             ftp.stream.put(chunk)
128         ftp.stream.put(None)
129         ftp.wait()
130
131         desc.executable = url
132     if params['nodes']:
133         desc.set_nodes(*params['nodes'])
134     if params['reservation']:
135         desc.set_reservation(params['reservation'])
136     if params['watch_output']:
137         desc.set_watch_output(params['watch_output'], params['watch_output_pattern'])
138
139     # TODO monitoring
140     
141     return desc
142
143
144 def to_form_data(xml):
145     # prevent circular import errors
146     from forms import JobDescriptionForm
147
148     QCG.start()
149     desc = JobDescription()
150     desc.xml_description = xml
151
152     direct_map = ('env_variables', 'executable', 'arguments', 'note', 'grant', 'hosts', 'properties', 'queue', 'procs',
153                   'wall_time', 'memory', 'memory_per_slot', 'modules', 'input', 'stage_in', 'native', 'persistent')
154
155     params = {}
156     for name in direct_map:
157         attr = getattr(desc, name)
158         if isinstance(attr, bool) or attr:
159             params[name] = attr
160
161     if desc.application is not None:
162         app_name, app_ver = desc.application
163         params['application'] = app_name if app_ver is None else app_name + '/' + app_name
164         stage_in = params['stage_in']
165         params['stage_in'], params['master_file'] = stage_in[:-1], stage_in[-1]
166         params['arguments'] = params['arguments'][1:]
167     if desc.nodes is not None:
168         params['nodes'] = ':'.join(map(str, desc.nodes))
169     if desc.reservation is not None:
170         res_id, res_type = desc.reservation
171         params['reservation'] = res_id
172     if desc.notify is not None:
173         params['notify_type'], params['notify_address'] = desc.notify.split(':')
174     if desc.watch_output is not None:
175         watch_output, params['watch_output_pattern'] = desc.watch_output
176         params['watch_output_type'], params['watch_output_address'] = watch_output.split(':')
177     if desc.preprocess is not None:
178         if desc.preprocess.startswith('gsiftp://'):
179             params['preprocess_type'] = JobDescriptionForm.Process.SCRIPT
180             params['preprocess_script'] = desc.preprocess
181         else:
182             params['preprocess_type'] = JobDescriptionForm.Process.CMD
183             params['preprocess_cmd'] = desc.preprocess
184     if desc.postprocess is not None:
185         if desc.postprocess.startswith('gsiftp://'):
186             params['postprocess_type'] = JobDescriptionForm.Process.SCRIPT
187             params['postprocess_script'] = desc.postprocess
188         else:
189             params['postprocess_type'] = JobDescriptionForm.Process.CMD
190             params['postprocess_cmd'] = desc.postprocess
191
192     return params
193
194
195 def restricted(view):
196     return wraps(view)(cache_control(no_cache=True, must_revalidate=True, no_store=True)(login_required(view)))