02c1e548805f178ee479a066111bbe1028e7c7b2
[chemdb.git] / chemdb / models.py
1 # Copyright
2
3 from django.db import models as _models
4 from django.forms import ValidationError as _ValidationError
5
6 from . import LOG as LOG
7 from . import util as _util
8
9
10 class NamedItem (_models.Model):
11     name = _models.CharField(max_length=100)
12     abbrev = _models.CharField('abbreviation', max_length=20)
13
14     class Meta:
15         abstract = True
16         ordering = ['name']
17
18     def __unicode__(self):
19         return u'{0.abbrev}'.format(self)
20
21
22 class NFPASpecial (NamedItem):
23     """An NFPA Special rating (e.g. 'OX', '-W-', 'SA', ...).
24     """
25     symbol = _models.CharField(max_length=3, blank=True, null=True)
26
27     def __unicode__(self):
28         if self.symbol:
29             return u'{0.symbol}'.format(self)
30         return super(NFPASpecial, self).__unicode__()
31
32
33 class CASNumber (NamedItem):
34     "Chemical Abstracts Service registery number"
35     cas = _models.CharField(
36         'CAS#', max_length=20, unique=True)
37
38     def clean(self):
39         if not _util.valid_CASno(self.cas):
40             raise _ValidationError("invalid CAS number '{}'".format(self.cas))
41
42
43 class Chemical (NamedItem):
44     """A chemical (in the abstract, not an instance of the chemical)
45
46     Separating ``Chemical``\s from ``ChemicalInstance``\s avoids
47     duplicate information (e.g. you can have two bottles of acetic
48     acid).
49     """
50     cas = _models.ManyToManyField(CASNumber, blank=True, null=True)
51     msds = _models.FileField(
52         'Material safety data sheet', upload_to=_util.chemical_upload_to,
53         blank=True, null=True)
54     health = _models.PositiveIntegerField(
55         'NFPA health rating', blank=True, null=True)
56     fire = _models.PositiveIntegerField(
57         'NFPA fire rating', blank=True, null=True)
58     reactivity = _models.PositiveIntegerField(
59         'NFPA reactivity rating', blank=True, null=True)
60     special = _models.ManyToManyField(NFPASpecial, blank=True, null=True)
61     mutagen = _models.NullBooleanField()
62     carcinogen = _models.NullBooleanField()
63     teratogen = _models.NullBooleanField()
64     note = _models.TextField('notes', blank=True, null=True)
65
66     def cas_numbers(self):
67         if self.cas.count() == 0:
68             return 'unknown'
69         return ', '.join(cas.cas for cas in self.cas.all())
70
71     def specials(self):
72         return ' '.join(str(special) for special in self.special.all())
73
74
75 class Location (NamedItem):
76     "A chemical storage location (e.g. 'acidic liquids')"
77     pass
78
79
80 class Vendor (NamedItem):
81     "A chemical supplier"
82     url = _models.URLField('URL', blank=True, null=True)
83     note = _models.TextField('notes', blank=True, null=True)
84
85
86 class ChemicalInstance (_models.Model):
87     """An instance of a ``Chemical``
88
89     For example, 1L of acetic acid from Vendor X.
90     """
91     chemical = _models.ForeignKey(Chemical, related_name='chemical_instances')
92     location = _models.ForeignKey(Location, related_name='chemical_instances')
93     amount = _models.CharField(max_length=100)
94     vendor = _models.ForeignKey(Vendor, related_name='chemical_instances')
95     catalog = _models.CharField('vendor catalog number', max_length=100)
96     received = _models.DateField(auto_now_add=True, editable=True)
97     disposed = _models.DateField(blank=True, null=True)
98
99     class Meta:
100         ordering = ['chemical', 'received', 'disposed', 'id']