Example #1
0
class Template(db.Model, BaseMixin):
    __tablename__ = 'templates'

    id = Column(db.Integer, primary_key=True)
    display_name = Column(db.String(STRING_LEN))
    file_name = Column(db.String(STRING_LEN))
    html = Column(db.Text())
    url = Column(db.String(STRING_LEN))
    active = Column(db.Integer)
    order = Column(db.Integer)
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())

    @classmethod
    def get_all(cls):
        return [x for x in cls.query.order_by(cls.order.asc()).all()]

    @classmethod
    def get_all_choices(cls):
        return [(t.id, t.file_name, t.display_name) for t in cls.get_all()]

    @classmethod
    def get_html(cls, template_id):
        template = cls.query.filter_by(id=template_id).first()
        if template:
            path = os.path.abspath(
                os.path.join(current_app.config.get('PROJECT_ROOT'), 'static',
                             'templates', template.file_name + ".html"))
            with open(path) as myfile:
                html = myfile.read()
            return html
        else:
            return None
Example #2
0
class Role(db.Model, RoleMixin, BaseMixin):
    __tablename__ = 'roles'

    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

    @classmethod
    def find_by_name(cls, name):
        return cls.query.filter_by(name=name).first()
Example #3
0
class Blacklist(db.Model, BaseMixin):
    __tablename__ = 'blacklists'

    id = Column(db.Integer, primary_key=True)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    email = Column(db.String(STRING_LEN), index=True)
    detail = Column(db.String(STRING_LEN))
    reason = Column(db.String(STRING_LEN))
    manual = Column(db.Integer, default=0)
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())

    @classmethod
    def check(cls, account_id, email):
        return cls.query.with_entities(cls.id).filter(
            cls.account_id == account_id, cls.email == email).count() or 0

    @classmethod
    def insert(cls, account_id, email, reason, detail):
        if not Blacklist.check(account_id, email):
            if detail:
                detail = (detail[:240] + '..') if len(detail) > 240 else detail
            if reason:
                reason = (reason[:240] + '..') if len(reason) > 240 else reason
            Blacklist.create(account_id=account_id,
                             email=email,
                             reason=reason,
                             detail=detail)
            return True
        else:
            return False

    @classmethod
    def save_file(cls, account_id, file):
        filename = secure_filename(file.filename)
        if current_app.debug:
            # Local Save
            account_path = os.path.join(current_app.config['IMPORT_FOLDER'],
                                        str(account_id))
            make_dir(account_path)
            blacklist_path = os.path.join(account_path, 'blacklists')
            make_dir(blacklist_path)
            file_path = os.path.join(blacklist_path, filename)
            file.save(file_path)
            return file_path
        else:
            # S3 Save
            return upload_blacklist(account_id, filename, file)
Example #4
0
class Misc(db.Model, BaseMixin):
    __tablename__ = 'misc'

    id = Column(db.Integer, primary_key=True)
    name = Column(db.String(STRING_LEN))
    data = Column(JSON)
    count = Column(db.Integer)
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())

    @classmethod
    def do_stuff(self):
        '''
        Used for load testing
        '''
        r = self.create(name='create_random',
                        data={
                            'dog': 'cat',
                            'black': 'white'
                        })
        r2 = Misc.find_by_id_anon(r.id)
        r2.update(data={})
        r2.update(data={'yes': 'no'})
        r2.save()
        r2.delete()
        r2 = Misc.find_by_id_anon(r.id)
        redis.incr('load_test')
Example #5
0
class User(db.Model, UserMixin, BaseMixin):
    __tablename__ = 'users'

    id = Column(db.Integer, primary_key=True)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    account = relationship("Account",
                           primaryjoin="Account.id==User.account_id",
                           foreign_keys="User.account_id")
    email = Column(db.String(STRING_LEN), nullable=False, unique=True)
    first_name = Column(db.String(STRING_LEN), nullable=False)
    last_name = Column(db.String(STRING_LEN), nullable=False)
    accepted_terms = Column(db.SmallInteger, default=0)
    activation_key = Column(db.String(STRING_LEN))
    password = Column(db.String(STRING_LEN), nullable=False)
    phone = Column(db.String(STRING_LEN))
    image = Column(db.String(STRING_LEN))
    confirmed_at = Column(db.DateTime(timezone=True))
    login_count = Column(db.Integer)
    current_login_at = Column(db.DateTime(timezone=True))
    last_login_at = Column(db.DateTime(timezone=True))
    current_login_ip = Column(db.String(STRING_LEN))
    last_login_ip = Column(db.String(STRING_LEN))
    active = db.Column(db.Boolean(), default=True)
    roles = db.relationship('Role',
                            secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())

    def __repr__(self):
        return '<User: %s>' % self.email

    @property
    def name(self):
        return ' '.join(
            [self.first_name.capitalize(),
             self.last_name.capitalize()])

    @classmethod
    def get_by_id(cls, user_id):
        return cls.query.filter_by(id=user_id).first_or_404()
Example #6
0
class Unsubscribe(db.Model, BaseMixin):
    __tablename__ = 'unsubscribes'

    id = Column(db.Integer, primary_key=True)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    email = Column(db.String(STRING_LEN), index=True)
    manual = Column(db.Integer, default=0)
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())

    @classmethod
    def check(cls, account_id, email):
        return cls.query.with_entities(cls.id).filter(
            cls.account_id == account_id, cls.email == email).count() or 0

    @classmethod
    def get_all_emails(cls):
        unsubscribes = cls.find_all().all()
        emails = []
        for unsub in unsubscribes:
            emails.append(unsub.email)
        return emails

    @classmethod
    def generate_csv(cls):
        content = StringIO.StringIO()
        cw = csv.writer(content)

        unsubscribes = cls.find_all().all()

        for unsub in unsubscribes:
            created = arrow.get(
                unsub.created_at).to('US/Arizona').format('MM/DD/YY HH:mm:ss')
            cw.writerow([unsub.email, created])

        ot = arrow.get(datetime.utcnow())
        ot = ot.to('US/Arizona').format('MM/DD/YY HH:mm:ss')
        filename = "unsubscribes-{}.csv".format(ot)
        response = make_response(content.getvalue())
        response.headers[
            "Content-Disposition"] = "attachment; filename=%s" % filename
        response.headers["Content-type"] = "text/csv"
        return response
Example #7
0
class List(db.Model, BaseMixin):
    __tablename__ = 'lists'

    id = Column(db.Integer, primary_key=True)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    name = Column(db.String(STRING_LEN), nullable=False)
    filename = Column(db.String(STRING_LEN))
    import_data = deferred(db.Column(JSON))
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())
    account = relationship("Account",
                           primaryjoin="Account.id==List.account_id",
                           foreign_keys="List.account_id")
    campaigns = relationship("Campaign",
                             primaryjoin="Campaign.list_id==List.id",
                             foreign_keys="Campaign.list_id")

    def __repr__(self):
        return '<List: %s - %s>' % (self.id, self.name)

    @classmethod
    def save_file(cls, file, list_id, curr_acct_id):
        if current_app.debug:
            # Local Save
            filename = secure_filename(file.filename)
            account_path = os.path.join(current_app.config['IMPORT_FOLDER'],
                                        str(curr_acct_id))
            make_dir(account_path)
            list_path = os.path.join(account_path, str(list_id))
            make_dir(list_path)
            file_path = os.path.join(list_path, filename)
            file.save(file_path)
            return file_path
        else:
            # S3 Save
            filename = secure_filename(file.filename)
            return upload_list(curr_acct_id, list_id, filename, file)

    def total_send_count(self):
        return len(self.import_data) if self.import_data else 0

    def get_import_data_keys(self):
        if self.import_data:
            keys = []
            for k, v in self.import_data[0].iteritems():
                if k != "hash_id":
                    keys.append(k)
            keys.sort()
            return keys
        else:
            return []

    def get_unique_col_values(self, col, tup=False):
        if self.import_data:
            vals = set([])
            for row in self.import_data:
                if row.get(col):
                    vals.add(row.get(col))
            if tup:
                cover = [('', '')]
                for v in vals:
                    cover.append((v, v))
                return cover
            else:
                return vals
        else:
            return []

    def random_data_sample(self, sample_size=15):
        if self.import_data:
            random_set = range(len(self.import_data))
            sample_size = sample_size if len(self.import_data) >= 15 else len(
                self.import_data)
            randos = random.sample(random_set, sample_size)
            random_data = []
            for r in randos:
                random_data.append(self.import_data[r])
            return random_data
        else:
            return None
Example #8
0
class Email(db.Model, BaseMixin):
    __tablename__ = 'emails'

    id = Column(db.Integer, primary_key=True)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    selector_col_val = Column(db.String(STRING_LEN))
    name = Column(db.String(STRING_LEN), nullable=False)
    subject = Column(db.String(STRING_LEN), nullable=True)
    preheader = Column(db.String(STRING_LEN))
    html = Column(db.Text())
    text = Column(db.Text())
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())
    account = relationship("Account",
                           primaryjoin="Account.id==Email.account_id",
                           foreign_keys="Email.account_id")
    campaign_id = Column(db.Integer,
                         db.ForeignKey('campaigns.id'),
                         nullable=False)
    campaign = relationship("Campaign",
                            primaryjoin="Campaign.id==Email.campaign_id",
                            foreign_keys="Email.campaign_id")
    sends = relationship("Send",
                         primaryjoin="Email.id==Send.email_id",
                         foreign_keys="Send.email_id")

    def full_html(self):
        footer = self.account.footer_html
        html = self.html
        if footer or self.preheader:
            soup = BeautifulSoup(html)
            if soup.body:
                if footer:
                    soup.body.append(BeautifulSoup(footer))
                if self.preheader:
                    preheader = "<span class='preheader'>%s</span>" % self.preheader
                    soup.body.insert(0, preheader)
                html = unicode(soup.html)
            else:
                if footer:
                    html = "%s<br>%s" % (html, footer)
                if self.preheader:
                    preheader = "<span class='preheader'>%s</span>" % self.preheader
                    html = preheader + html
        return urllib.unquote(html)

    def full_text(self):
        footer = self.account.footer_text
        text = self.text
        if footer:
            text = "%s\n\n%s" % (text, footer)
        return urllib.unquote(text)

    def render_html_attr(self, data, hash_id):
        data = Email.add_additional_data(data, hash_id)
        html = self.full_html()
        return pystache.render(html, data)

    def render_text_attr(self, data, hash_id):
        data = Email.add_additional_data(data, hash_id)
        text = self.full_text()
        return pystache.render(text, data)

    def check_keys(self, list_id):
        list_ = List.find_by_id_anon(list_id)
        keys = list_.random_data_sample(1)[0]
        renderer = pystache.Renderer(missing_tags='strict')
        try:
            renderer.render(self.html, keys)
            return True, None
        except KeyNotFoundError, e:
            return False, e.key
Example #9
0
class Campaign(db.Model, BaseMixin):
    __tablename__ = 'campaigns'

    id = Column(db.Integer, primary_key=True)
    name = Column(db.String(STRING_LEN), nullable=False)
    account_id = Column(db.Integer,
                        db.ForeignKey('accounts.id'),
                        nullable=False)
    selector_col_name = Column(db.String(STRING_LEN))
    from_email_dd = Column(db.String(STRING_LEN))  # dd = dynamic data
    from_name_dd = Column(db.String(STRING_LEN))
    reply_to_dd = Column(db.String(STRING_LEN))
    to_email_dd = Column(db.String(STRING_LEN))
    to_name_dd = Column(db.String(STRING_LEN))
    from_email_ov = Column(db.String(STRING_LEN))  # ov = overwrite
    from_name_ov = Column(db.String(STRING_LEN))
    reply_to_ov = Column(db.String(STRING_LEN))
    to_email_ov = Column(db.String(STRING_LEN))
    to_name_ov = Column(db.String(STRING_LEN))
    created_at = Column(db.DateTime(timezone=True), default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True),
                         default=db.func.now(),
                         onupdate=db.func.now())
    account = relationship("Account",
                           primaryjoin="Account.id==Campaign.account_id",
                           foreign_keys="Campaign.account_id")
    list_id = Column(db.Integer)
    list_ = relationship("List",
                         primaryjoin="List.id==Campaign.list_id",
                         foreign_keys="Campaign.list_id")
    emails = relationship("Email",
                          primaryjoin="Campaign.id==Email.campaign_id",
                          foreign_keys="Email.campaign_id")
    dispatcher = relationship(
        "Dispatcher",
        primaryjoin="Campaign.id==Dispatcher.campaign_id",
        foreign_keys="Dispatcher.campaign_id")

    def __repr__(self):
        return '<Campaign: %s - %s>' % (self.id, self.name)

    def check_email_keys(self):
        for email in self.emails:
            valid, bad_key = email.check_keys(self.list_id)
            if not valid:
                return False, bad_key
        return True, None

    def render_attr(self, attr, data):
        return pystache.render("{{ %s }}" % getattr(self, attr), data)

    def email_determiner(self, data):
        ''' Find which of the campaign's email's this data runs with
            returns first one found
        '''
        if self.selector_col_name and len(self.selector_col_name) > 0:
            col_name_val = data.get(self.selector_col_name)
            if col_name_val and len(col_name_val) > 0:
                for email in self.emails:
                    if email.selector_col_val:
                        vals = json.loads(email.selector_col_val)
                        for val in vals:
                            if val and len(val) > 0 and col_name_val == val:
                                return email
        else:
            return self.emails[0] if len(self.emails) else None

    def get_selector_import_data(self):
        selector_data = []
        for data in self.list_.import_data:
            if self.email_determiner(data):
                selector_data.append(data)
        return selector_data

    def selector_send_count(self):
        key = 'selector_send_count_%s' % self.id
        val = get_cache(key)
        if val:
            return val
        else:
            count = 0
            for data in self.list_.import_data:
                if self.email_determiner(data):
                    count = count + 1
            set_cache(key, count, 10)
            return count

    def determiner_duplicates(self):
        ''' Used so one person doesn't get multiple emails.
            Returns (True, val) if dups or (false, None) if its ok.
        '''
        values = []
        for email in self.emails:
            if email.selector_col_val:
                vals = json.loads(email.selector_col_val)
                for val in vals:
                    if val and val in values:
                        return True, val
                    else:
                        values.append(val)
        return False, None

    def selector_missing(self):
        if len(self.emails) > 1 and not self.selector_col_name:
            return True
        return False
Example #10
0
class Account(db.Model, BaseMixin):
    __tablename__ = 'accounts'

    id = Column(db.Integer, primary_key=True)
    name = Column(db.String(STRING_LEN), nullable=False, unique=True)
    domain = Column(db.String(STRING_LEN), nullable=False)
    subaccount_name = Column(db.String(STRING_LEN))
    api_key = Column(db.String(STRING_LEN))
    dedicated_ip = Column(db.String(STRING_LEN))
    active = Column(db.SmallInteger, default=1)
    default_from_email = Column(db.String(STRING_LEN))
    contact_email = Column(db.String(STRING_LEN))
    phone = Column(db.String(STRING_LEN))
    address = Column(db.String(STRING_LEN))
    city = Column(db.String(STRING_LEN))
    state = Column(db.String(STRING_LEN))
    zip_code = Column(db.String(STRING_LEN))
    country = Column(db.Integer)
    auto_text = Column(db.Integer)
    footer_html = Column(db.Text())
    footer_text = Column(db.Text())
    created_at = Column(db.DateTime(timezone=True),  default=db.func.now())
    modified_at = Column(db.DateTime(timezone=True), default=db.func.now(), onupdate=db.func.now())


    def ip_pool(self):
        return self.dedicated_ip or None


    @classmethod
    def find_by_name(cls, name):
        return cls.query.filter_by(name=name).first()