Save job logs, repport the size
[ais.git] / bin / djais / models.py
1 # -*- coding: utf-8 -*-
2
3 from __future__ import division
4 import os, os.path
5 from datetime import datetime
6 from django.db import models
7 from django.contrib.auth.models import get_hexdigest
8 from django.utils import html
9
10 from ais.common import Nmea, mmsi_to_strmmsi, nice_timedelta_str
11 from ais import proc
12 from ais.jobrunner import make_unique_job_id
13
14 class UserMessageCategory(models.Model):
15     id = models.CharField(max_length=10, primary_key=True)
16     class Meta:
17         db_table = u'user_message_category'
18
19 class UserMessage(models.Model):
20     id = models.AutoField(primary_key=True)
21     user = models.ForeignKey('User')
22     category = models.ForeignKey(UserMessageCategory, db_column='user_message_category_id')
23     txt = models.TextField()
24     class Meta:
25         db_table = u'user_message'
26
27 class User(models.Model):
28     id = models.AutoField(primary_key=True)
29     login = models.CharField(max_length=16, unique=True)
30     password_hash = models.CharField(max_length=75)
31     name = models.CharField(max_length=50)
32     email = models.EmailField()
33     father = models.ForeignKey('User')
34     creation_datetime = models.DateTimeField(auto_now_add=True)
35     phone = models.CharField(max_length=20, blank=True)
36     access_datetime = models.DateTimeField(blank=True, null=True)
37     class Meta:
38         db_table = u'user'
39         ordering = ('id',)
40
41     def __unicode__(self):
42         return self.login
43
44     def set_password(self, raw_password):
45         import random
46         algo = 'sha1'
47         salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
48         hsh = get_hexdigest(algo, salt, raw_password)
49         self.password_hash = '%s$%s$%s' % (algo, salt, hsh)
50         self.info('Password changed') # FIXME
51
52     def check_password(self, raw_password):
53         password_hash = self.password_hash
54         if not password_hash:
55             return False
56         algo, salt, hsh = password_hash.split('$')
57         return hsh == get_hexdigest(algo, salt, raw_password)
58
59
60     def update_access_datetime(self):
61         self.access_datetime = datetime.utcnow()
62         self.save()
63
64     def get_and_delete_messages(self):
65         return None
66
67     def is_admin_by(self, user_id):
68         if self.id == user_id:
69             return True
70         if self.father_id is None:
71             return False
72         return self.father.is_admin_by(user_id)
73
74     def get_messages(self):
75         messages = UserMessage.objects.filter(user__id = self.id)
76         messages_dict = \
77             [ {'category': message.category, \
78                'txt': message.txt} \
79             for message in messages \
80             ]
81         messages.delete()
82         return messages_dict
83     
84     def info(self, message):
85         UserMessage(user_id = self.id, category_id=u'info', txt=html.escape(message)).save()
86
87     def error(self, message):
88         UserMessage(user_id = self.id, category_id=u'error', txt=html.escape(message)).save()
89
90     def check_sandbox_access(self, source_user=None):
91         SANDBOX_FLEET = 1
92         try:
93             FleetUser.objects.get(fleet = SANDBOX_FLEET, user = self)
94         except FleetUser.DoesNotExist:
95             fu = FleetUser()
96             fu.user_id = self.id
97             fu.fleet_id = SANDBOX_FLEET
98             fu.save()
99             if source_user:
100                 source_user.info("%s was granted access to 'sandbox' fleet" % self.login)
101
102     def fleets_count(self):
103         return FleetUser.objects.filter(user=self.id).count()
104
105
106 class Vessel(models.Model):
107     mmsi = models.IntegerField(primary_key=True)
108     name = models.CharField(max_length=20)
109     imo = models.IntegerField()
110     callsign = models.CharField(max_length=7)
111     type = models.IntegerField(default=0)
112     destination = models.CharField(max_length=20, blank=True, null=True)
113     updated = models.DateTimeField()
114     source = models.CharField(max_length=8)
115     dim_bow = models.IntegerField(default=0)
116     dim_stern = models.IntegerField(default=0)
117     dim_port = models.IntegerField(default=0)
118     dim_starboard = models.IntegerField(default=0)
119     eta = models.CharField(max_length=8, default='00002460') # format MMDDhhmm
120     class Meta:
121         db_table = u'vessel'
122     def __unicode__(self):
123         return unicode(self.mmsi) # FIXME
124     def get_last_nmea(self):
125         strmmsi = mmsi_to_strmmsi(self.mmsi)
126         return Nmea.new_from_lastinfo(strmmsi)
127
128
129 class Fleet(models.Model):
130     id = models.AutoField(primary_key=True)
131     name = models.CharField(max_length=50)
132     vessel = models.ManyToManyField(Vessel, through='FleetVessel')
133     description = models.TextField()
134     class Meta:
135         db_table = u'fleet'
136     def __unicode__(self):
137         return self.name
138     def vessel_count(self):
139         return FleetVessel.objects.filter(fleet=self.id).count()
140     def user_count(self):
141         return FleetUser.objects.filter(fleet=self.id).count()
142     def job_count(self):
143         if os.path.exists('/var/lib/ais/cron/fleets/%s.cron' % self.name):
144             return 1
145         else:
146             return 0
147
148 class FleetUser(models.Model):
149     id = models.AutoField(primary_key=True)
150     fleet = models.ForeignKey(Fleet) #, db_column='fleet_id', to_field='id')
151     user = models.ForeignKey(User)
152     class Meta:
153         db_table = u'fleet_user'
154
155 class FleetVessel(models.Model):
156     id = models.AutoField(primary_key=True)
157     fleet = models.ForeignKey(Fleet, db_column='fleet_id', to_field='id')
158     vessel = models.ForeignKey(Vessel, db_column='mmsi', to_field='mmsi')
159     class Meta:
160         db_table = u'fleet_vessel'
161     
162 ## manual input source
163 #class MiSource(models.Model):
164 #    id = models.IntegerField(primary_key=True)
165 #    userid = models.IntegerField()
166 #    name = models.TextField(unique=True)
167 #    class Meta:
168 #        db_table = u'mi_source'
169 #
170 ## manual input vessel
171 #class MiVessel(models.Model):
172 #    mmsi_txt = models.TextField(primary_key=True) # This field type is a guess.
173 #    class Meta:
174 #        db_table = u'mi_vessel'
175
176
177 # Plane plotter
178 #class Ppuser(models.Model):
179 #    usr = models.TextField(primary_key=True) # This field type is a guess.
180 #    lat = models.FloatField()
181 #    lon = models.FloatField()
182 #    class Meta:
183 #        db_table = u'ppuser'
184 #
185 #class Plane(models.Model):
186 #    flight = models.CharField(max_length=8)
187 #    reg = models.CharField(max_length=8)
188 #    ads = models.CharField(max_length=8)
189 #    type = models.CharField(max_length=4)
190 #    usr = models.TextField() # This field type is a guess.
191 #    updated = models.DateTimeField()
192 #    class Meta:
193 #        db_table = u'plane'
194
195
196
197 class News(models.Model):
198     id = models.AutoField(primary_key=True)
199     created = models.DateTimeField()
200     updated = models.DateTimeField()
201     title = models.TextField()
202     txt = models.TextField()
203     class Meta:
204         db_table = u'news'
205
206
207 class Job(models.Model):
208     id = models.CharField(primary_key=True, max_length=8, default=make_unique_job_id)
209     user = models.ForeignKey(User)
210     queue_time = models.DateTimeField() #auto_now_add=True) is buggy, not GMT
211     start_time = models.DateTimeField(blank=True, null=True)
212     finish_time = models.DateTimeField(blank=True, null=True)
213     command = models.TextField()
214     friendly_filename = models.CharField(max_length=255)
215     pid = models.IntegerField(blank=True, null=True)
216     result = models.IntegerField(blank=True, null=True)
217     archive_time = models.DateTimeField(blank=True, null=True)
218     notify = models.CharField(max_length=1, blank=True, null=True)
219
220     def queue_rank(self):
221         return Job.objects.filter(queue_time__lt=self.queue_time).filter(start_time__isnull=True).count() + 1
222
223     @staticmethod
224     def queue_size():
225         return Job.objects.filter(start_time__isnull=True).count()
226
227     def process_time(self):
228         dt = self.finish_time - self.start_time
229         return nice_timedelta_str(dt)
230
231     def running_time(self):
232         dt = datetime.utcnow() - self.start_time
233         return nice_timedelta_str(dt)
234
235     def get_stats(self):
236         try:
237             return proc.Stat(self.pid)
238         except:
239             return
240     
241     def get_sucess_size(self):
242         extension = os.path.splitext(self.friendly_filename)[-1]
243         filename = '/var/lib/ais/jobs/%s%s' % (self.id, extension)
244         return os.path.getsize(filename)
245
246     def get_log_size(self):
247         filename = '/var/lib/ais/jobs/%s.log' % self.id
248         try:
249             return os.path.getsize(filename)
250         except OSError, err:
251             if err.errno == 2:
252                 return 0
253             raise
254
255     class Meta:
256         db_table = u'job'
257         ordering = ('queue_time',)