In [8]:
import operator
import os
import re
import sys

from numpy.linalg import solve, lstsq
import numpy as np

from sqlalchemy import orm, Column, Integer, String, Float, create_engine, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, backref
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method

_minwidth = 15

Base = declarative_base()

class BaseChemical(object):

    def formula_to_tex(self):
        '''
        Convert the formula string to tex string.
        '''
        return re.sub(ur'(\d+)', ur'$_{\1}$', self.formula)
    
    def formula_to_html(self):
        '''
        Convert the formula string to html string.
        '''
        return re.sub(ur'(\d+)', ur'<sub>\1</sub>', self.formula)
    
    def listctrl_label(self):
        '''
        Return the string to be displayed in the ListCtrl's.
        '''
        if self.is_undefined(self.short_name):
            res = self.name
        else:
            res = self.short_name
        return res
    
    def label(self):
        '''
        Return a label to be used in printable tables (tex, html).
        '''
        if self.is_undefined(self.short_name):
            res = self.formula_to_tex() + u" ({0:>4.1f}\%)".format(100*self.concentration)
        else:
            res = self.short_name + u" ({0:>4.1f}\%)".format(100*self.concentration)
        return res

    @staticmethod
    def is_undefined(item):
        if item is None or item.lower() in ["", "null", "none"]:
            return True
        else:
            return False
    
class Category(Base):
    __tablename__ = 'categories'

    id        = Column(Integer, primary_key=True)
    name      = Column(String, nullable=False)
    full_name = Column(String)

    def __repr__(self):
        return "<Category(id={i}, name={n}, full_name={f})>".format(i=self.id,
                n=self.name, f=self.full_name)

class Electrolyte(Base):
    __tablename__ = 'electrolytes'

    id   = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

    def __repr__(self):
        return "<Electrolyte(id={i}, form={n})>".format(i=self.id, n=self.name)

class PhysicalForm(Base):
    __tablename__ = 'physical_forms'

    id   = Column(Integer, primary_key=True)
    form = Column(String, nullable=False)

    def __repr__(self):
        return "<PhysicalForm(id={i}, form={n})>".format(i=self.id, n=self.form)

class Kind(Base):
    __tablename__ = 'kinds'

    id   = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

    def __repr__(self):
        return "<Kind(id={i}, name={n})>".format(i=self.id, n=self.name)

class Reaction(Base):
    __tablename__ = 'reactions'

    id       = Column(Integer, primary_key=True)
    reaction = Column(String, nullable=False)

    def __repr__(self):
        return "<Reaction(id={i}, reaction={n})>".format(i=self.id, n=self.reaction)

class Batch(Base):
    __tablename__ = 'batch'

    id           = Column(Integer, primary_key=True)
    chemical_id  = Column(Integer, ForeignKey('chemicals.id'), nullable=False)
    component_id = Column(Integer, ForeignKey('components.id'), nullable=False)
    reaction_id  = Column(Integer, ForeignKey('reactions.id'), nullable=True)
    coefficient  = Column(Float, nullable=True)
    
    chemical = relationship("Chemical")
    component = relationship("Component")
    reaction= relationship("Reaction")

    def __repr__(self):
        return "<Batch(id={i:>2d}, chemical='{n:s}', component='{z:s}', coefficient={c:8.2f})>".format(
                i=self.id, n=self.chemical.name, z=self.component.name, c=self.coefficient)

class Component(BaseChemical, Base):
    '''
    Class representing the Component object. The component can belong to one of
    the 3 categories:
        * Zeolite component
        * Template
        * Zeolite Growth Modifier
    '''
    __tablename__ = 'components'

    id          = Column(Integer, primary_key=True)
    name        = Column(String, nullable=False)
    formula     = Column(String, nullable=False)
    molwt       = Column(Float, nullable=False)
    short_name  = Column(String)

    _category_id = Column("category_id", Integer, ForeignKey('categories.id'))
    _category = relationship("Category")
    category = association_proxy("_category", "name")

    @orm.reconstructor
    def init_on_load(self):
        self.moles = 0.0
    
    @hybrid_property
    def mass(self):
        return self.moles*self.molwt
    
    def __repr__(self):
        return "<Component(id={i:>2d}, name='{n:s}', formula='{f:s}')>".format(
                i=self.id, n=self.name, f=self.formula)

class Chemical(BaseChemical, Base):
    '''
    Class representing the Chemical object, (off the shelf reactants).
    '''
    __tablename__ = 'chemicals'

    id            = Column(Integer, primary_key=True)
    name          = Column(String, nullable=False)
    formula       = Column(String, nullable=False)
    molwt         = Column(Float, nullable=False)
    short_name    = Column(String)
    concentration = Column(Float)
    cas           = Column(String)
    density       = Column(Float)
    pk            = Column(Float)
    smiles        = Column(String)
    
    _kind_id       = Column("kind_id", Integer, ForeignKey('kinds.id'))
    _kind = relationship("Kind")
    kind = association_proxy("_kind", "name")
    
    _electrolyte_id = Column("electrolyte_id", Integer, ForeignKey('electrolytes.id'))
    _electrolyte = relationship("Electrolyte")
    electrolyte = association_proxy("_electrolyte", "name")
    
    _physical_form_id = Column("physical_form_id", Integer, ForeignKey('physical_forms.id'))
    _physical_form = relationship("PhysicalForm")
    physical_form = association_proxy("_physical_form", "form")
        
    @orm.reconstructor
    def init_on_load(self):
        self.mass = 0.0
        
    @hybrid_property
    def moles(self):
        return self.mass/self.molwt

    @hybrid_property
    def volume(self):
        return self.mass/self.density
    
    def __repr__(self):
        return "<Chemical(id={i:>2d}, name='{n:s}', formula='{f:s}')>".format(
                i=self.id, n=self.name, f=self.formula)

In [2]:
engine = create_engine("sqlite:///{path:s}".format(path="zeolite.db"))
DBSession  = sessionmaker(bind=engine)
dbsession = DBSession()

In [9]:
naoh = dbsession.query(Batch).filter(Batch.chemical_id == 1).all()
naoh


Out[9]:
[<Batch(id= 1, chemical='sodium hydroxide', component='sodium oxide', coefficient=    0.50)>,
 <Batch(id= 2, chemical='sodium hydroxide', component='water', coefficient=    0.50)>]

In [10]:
comps = dbsession.query(Component).filter(Component.id <= 5).all()
comps


Out[10]:
[<Component(id= 1, name='sodium oxide', formula='Na2O')>,
 <Component(id= 2, name='potassium oxide', formula='K2O')>,
 <Component(id= 3, name='aluminium oxide', formula='Al2O3')>,
 <Component(id= 4, name='silicone dioxide', formula='SiO2')>,
 <Component(id= 5, name='water', formula='H2O')>]

In [18]:
compset = set()
for comp in comps:
    temp = dbsession.query(Chemical).join(Batch).\
                filter(Batch.component_id == comp.id).all()
    compset.update(temp)
    query = sorted(list(compset), key=lambda x: x.id)

In [19]:
query


Out[19]:
[<Chemical(id= 1, name='sodium hydroxide', formula='NaOH')>,
 <Chemical(id= 2, name='potassium hydroxide', formula='KOH')>,
 <Chemical(id= 3, name='sodium aluminate', formula='Na2Al2O4')>,
 <Chemical(id= 4, name='aluminium hydroxide', formula='Al(OH)3')>,
 <Chemical(id= 5, name='aluminium isopropoxide', formula='Al(OC3H7)3')>,
 <Chemical(id= 6, name='aluminum sulfate hexadecahydrate', formula='Al2(SO4)3*18H2O')>,
 <Chemical(id= 7, name='colloidal silica AM-30', formula='SiO2')>,
 <Chemical(id= 8, name='colloidal silica HS-40', formula='SiO2')>,
 <Chemical(id= 9, name='fumed silica', formula='SiO2')>,
 <Chemical(id=10, name='water', formula='H2O')>,
 <Chemical(id=12, name='tetramethylammonium hydroxide pentahydride', formula='(CH3)4NOH*5H2O')>]

In [7]:
comps = self.session.query(BComponent).\
            filter(Batch.chemical_id == chemical.id).\
            filter(Component.id == Batch.component_id).all()


Out[7]:
u'zeolite component'

In [8]:
b = dbsession.query(Batch).first()

In [9]:
b.component.name


Out[9]:
u'sodium oxide'

In [10]:
petp = Chemical(name="pentaerythritol", formula="C5H12O4", molwt=136.15, short_name="PETP", concentration=0.98, cas="115-77-5",
                density=1.399, pk=14.1)

In [11]:
petp._kind = dbsession.query(Kind).get(3)

In [12]:
petp._kind


Out[12]:
<Kind(id=3, name=reactant)>

In [13]:
petp._physical_form = dbsession.query(PhysicalForm).get(1)

In [14]:
petp._physical_form


Out[14]:
<PhysicalForm(id=1, form=crystals)>

In [58]:
petp = dbsession.query(Chemical).get(21)
dbsession.delete(petp)
dbsession.query(Chemical).all()


Out[58]:
[<Chemical(id= 1, name='sodium hydroxide', formula='NaOH')>,
 <Chemical(id= 2, name='potassium hydroxide', formula='KOH')>,
 <Chemical(id= 3, name='sodium aluminate', formula='Na2Al2O4')>,
 <Chemical(id= 4, name='aluminium hydroxide', formula='Al(OH)3')>,
 <Chemical(id= 5, name='aluminium isopropoxide', formula='Al(OC3H7)3')>,
 <Chemical(id= 6, name='aluminum sulfate hexadecahydrate', formula='Al2(SO4)3*18H2O')>,
 <Chemical(id= 7, name='colloidal silica AM-30', formula='SiO2')>,
 <Chemical(id= 8, name='colloidal silica HS-40', formula='SiO2')>,
 <Chemical(id= 9, name='fumed silica', formula='SiO2')>,
 <Chemical(id=10, name='water', formula='H2O')>,
 <Chemical(id=11, name='tetramethylammonium chloride', formula='(CH3)4NCl')>,
 <Chemical(id=12, name='tetramethylammonium hydroxide pentahydride', formula='(CH3)4NOH*5H2O')>,
 <Chemical(id=13, name='hexamethylenediamine', formula='NH2(CH2)6NH2')>,
 <Chemical(id=14, name='choline chloride', formula='(CH3)3N(Cl)CH2CH2OH')>,
 <Chemical(id=15, name='ethanol', formula='C2H5OH')>,
 <Chemical(id=16, name='ethylene glycol', formula='C2H6O2')>,
 <Chemical(id=17, name='1-propanol', formula='C3H7OH')>,
 <Chemical(id=18, name='2-propanol', formula='C3H7OH')>,
 <Chemical(id=19, name='glycerol', formula='C3H8O3')>,
 <Chemical(id=20, name='1-butanol', formula='C4H9OH')>]

In [16]:
dbsession.commit()

In [59]:
dbsession.query(Chemical).all()


Out[59]:
[<Chemical(id= 1, name='sodium hydroxide', formula='NaOH')>,
 <Chemical(id= 2, name='potassium hydroxide', formula='KOH')>,
 <Chemical(id= 3, name='sodium aluminate', formula='Na2Al2O4')>,
 <Chemical(id= 4, name='aluminium hydroxide', formula='Al(OH)3')>,
 <Chemical(id= 5, name='aluminium isopropoxide', formula='Al(OC3H7)3')>,
 <Chemical(id= 6, name='aluminum sulfate hexadecahydrate', formula='Al2(SO4)3*18H2O')>,
 <Chemical(id= 7, name='colloidal silica AM-30', formula='SiO2')>,
 <Chemical(id= 8, name='colloidal silica HS-40', formula='SiO2')>,
 <Chemical(id= 9, name='fumed silica', formula='SiO2')>,
 <Chemical(id=10, name='water', formula='H2O')>,
 <Chemical(id=11, name='tetramethylammonium chloride', formula='(CH3)4NCl')>,
 <Chemical(id=12, name='tetramethylammonium hydroxide pentahydride', formula='(CH3)4NOH*5H2O')>,
 <Chemical(id=13, name='hexamethylenediamine', formula='NH2(CH2)6NH2')>,
 <Chemical(id=14, name='choline chloride', formula='(CH3)3N(Cl)CH2CH2OH')>,
 <Chemical(id=15, name='ethanol', formula='C2H5OH')>,
 <Chemical(id=16, name='ethylene glycol', formula='C2H6O2')>,
 <Chemical(id=17, name='1-propanol', formula='C3H7OH')>,
 <Chemical(id=18, name='2-propanol', formula='C3H7OH')>,
 <Chemical(id=19, name='glycerol', formula='C3H8O3')>,
 <Chemical(id=20, name='1-butanol', formula='C4H9OH')>]

In [64]:
petp_dict = {"name":"pentaerythritol", "formula":"C5H12O4", "molwt":136.15, "short_name":"PETP", "concentration":0.98, "cas":"115-77-5",
                "density":1.399, "pk":14.1, "_kind_id" :1}
pery = Chemical(**petp_dict)

In [33]:
pery.cas


Out[33]:
'115-77-5'

In [61]:
def add_chemical_record(session, data):
    """
    Add a Chemical record to the database, the data should be in the form of a dictionary:

    data = {'name' : 'water', 'formula' : 'H2O', 'molwt' : 18.0152,
            'kind_id' : 3, 'concentration' : 1.0, 'cas' : '7732-18-5',
            'physical_form_id' : 3, 'density' : 0.997}
    """

    chemical = Chemical(**data)

    if data.get("_kind_id", None) is not None:
        chemical._kind = session.query(Kind).get(data["_kind_id"])

    if data.get("_physical_form_id") is not None:
        chemical._physical_form = session.query(PhysicalForm).get(data["_physical_form_id"])

    if data.get("_electrolyte_id") is not None:
        chemical._electrolyte = session.query(Electrolyte).get(data["_electrolyte_id"])

    session.add(chemical)
    session.commit()

In [66]:
dbsession.rollback()
add_chemical_record(dbsession, petp_dict)

In [67]:
dbsession.query(Chemical).all()


Out[67]:
[<Chemical(id= 1, name='sodium hydroxide', formula='NaOH')>,
 <Chemical(id= 2, name='potassium hydroxide', formula='KOH')>,
 <Chemical(id= 3, name='sodium aluminate', formula='Na2Al2O4')>,
 <Chemical(id= 4, name='aluminium hydroxide', formula='Al(OH)3')>,
 <Chemical(id= 5, name='aluminium isopropoxide', formula='Al(OC3H7)3')>,
 <Chemical(id= 6, name='aluminum sulfate hexadecahydrate', formula='Al2(SO4)3*18H2O')>,
 <Chemical(id= 7, name='colloidal silica AM-30', formula='SiO2')>,
 <Chemical(id= 8, name='colloidal silica HS-40', formula='SiO2')>,
 <Chemical(id= 9, name='fumed silica', formula='SiO2')>,
 <Chemical(id=10, name='water', formula='H2O')>,
 <Chemical(id=11, name='tetramethylammonium chloride', formula='(CH3)4NCl')>,
 <Chemical(id=12, name='tetramethylammonium hydroxide pentahydride', formula='(CH3)4NOH*5H2O')>,
 <Chemical(id=13, name='hexamethylenediamine', formula='NH2(CH2)6NH2')>,
 <Chemical(id=14, name='choline chloride', formula='(CH3)3N(Cl)CH2CH2OH')>,
 <Chemical(id=15, name='ethanol', formula='C2H5OH')>,
 <Chemical(id=16, name='ethylene glycol', formula='C2H6O2')>,
 <Chemical(id=17, name='1-propanol', formula='C3H7OH')>,
 <Chemical(id=18, name='2-propanol', formula='C3H7OH')>,
 <Chemical(id=19, name='glycerol', formula='C3H8O3')>,
 <Chemical(id=20, name='1-butanol', formula='C4H9OH')>,
 <Chemical(id=21, name='pentaerythritol', formula='C5H12O4')>,
 <Chemical(id=22, name='pentaerythritol', formula='C5H12O4')>]

In [69]:
a = dbsession.query(Chemical).get(22)

In [70]:
a.kind


Out[70]:
u'mixture'

In [20]:
import wx
wx.CheckBox.