Esempio n. 1
0
 def get_ready_to_run(self):
     (ready, stamps, finals) = self.scan_ready_to_run()
     if ready:
         for info in ready:
             info.stamp = stamps[info.basename]
         with eagn.DB() as db:
             invalids = set()
             for mailing in (agn.Stream(ready).map(
                     lambda i: i.mailing).distinct().sorted()):
                 rq = db.querys(
                     'SELECT deleted '
                     'FROM mailing_tbl '
                     'WHERE mailing_id = :mailingID',
                     {'mailingID': mailing})
                 if rq is None:
                     agn.log(agn.LV_INFO, 'ready',
                             'Mailing %d no more existing' % mailing)
                     invalids.add(mailing)
                 elif rq.deleted:
                     agn.log(agn.LV_INFO, 'ready',
                             'Mailing %d is marked as deleted' % mailing)
                     invalids.add(mailing)
             if invalids:
                 for info in (agn.Stream(ready).filter(
                         lambda i: i.mailing in invalids)):
                     self.move(info.path, self.deleted)
                     self.move(info.stamp.path, self.deleted)
                 ready = (agn.Stream(ready).filter(
                     lambda i: i.mailing not in invalids).list())
     if ready:
         agn.log(agn.LV_INFO, 'ready',
                 '%s files are ready to send' % agn.numfmt(len(ready)))
     return ready
Esempio n. 2
0
 def processCompleted(self):
     max_count = 25000  # max # of records to delete per batch
     outdated = 24 * 60 * 60  # if record reached this value, the record is assumed to be done
     to_remove = []
     agn.log(agn.LV_DEBUG, 'proc', 'Start processing completed records')
     for (section, key, value) in self.mtrack:
         diff = int(time.time()) - value[self.mtrack.key_created]
         if diff < 3600:
             diffstr = '%d:%02d' % (diff // 60, diff % 60)
         else:
             diffstr = '%d:%02d:%02d' % (diff // 3600,
                                         (diff // 60) % 60, diff % 60)
         #
         if section != self.SEC_MTAID:
             agn.log(
                 agn.LV_DEBUG, 'proc', 'Ignore non %s record: %s:%s' %
                 (self.SEC_MTAID, section, key))
             continue
         #
         if not value.get('complete'):
             if diff < outdated:
                 agn.log(
                     agn.LV_DEBUG, 'proc/%s' % key,
                     'Ignore not (yet) completed record since %s' % diffstr)
                 continue
             agn.log(agn.LV_INFO, 'proc/%s' % key,
                     'Found outdated incomplete record since %s' % diffstr)
             value['outdated'] = True
         #
         self.completed(key, value, to_remove)
         #
         to_remove.append((section, key))
         if len(to_remove) >= max_count:
             agn.log(
                 agn.LV_INFO, 'proc',
                 'Reached limit of %s, defer further processing' %
                 agn.numfmt(max_count))
             break
     #
     if to_remove:
         agn.log(agn.LV_VERBOSE, 'proc',
                 'Remove %s processed keys' % agn.numfmt(len(to_remove)))
         for (section, key) in to_remove:
             self.mtrack.delete(section, key)
             agn.log(agn.LV_DEBUG, 'proc', 'Removed %s:%s' % (section, key))
         agn.log(agn.LV_VERBOSE, 'proc', 'Removed processed keys done')
Esempio n. 3
0
    def collectNewBounces(self):  #{{{
        agn.log(agn.LV_INFO, 'collect', 'Start collecting new bounces')

        iquery = 'INSERT INTO bounce_collect_tbl (customer_id, company_id, mailing_id, change_date) VALUES (:customer, :company, :mailing, current_timestamp)'
        insert = self.db.cursor()
        if insert is None:
            raise agn.error(
                'collectNewBounces: Failed to get new cursor for insertion')
        bquery = self.db.cursor()
        if bquery is None:
            raise agn.error(
                'collectNewBounces: Failed to get new cursor for bounce query')
        data = {}
        query = 'SELECT customer_id, company_id, mailing_id, detail FROM bounce_tbl WHERE %s ORDER BY company_id, customer_id' % self.timestamp.makeBetweenClause(
            'change_date', data)
        cur = [0, 0, 0, 0]
        (records, uniques, inserts) = (0, 0, 0)
        if bquery.query(query, data) is None:
            raise agn.error(
                'collectNewBounces: Failed to query bounce_tbl using: %s' %
                query)
        while cur is not None:
            try:
                record = bquery.next()
                records += 1
            except StopIteration:
                record = None
            if record is None or cur[0] != record[0] or cur[1] != record[1]:
                if record is not None:
                    uniques += 1
                if cur[0] > 0 and cur[3] >= 400 and cur[3] < 510:
                    parm = {
                        'customer': cur[0],
                        'company': cur[1],
                        'mailing': cur[2]
                    }
                    insert.update(iquery, parm)
                    inserts += 1
                    if inserts % 10000 == 0 or record is None:
                        agn.log(
                            agn.LV_DEBUG, 'collect',
                            'Inserted now %s record%s' %
                            (agn.numfmt(inserts), exts(inserts)))
                        insert.sync()
                cur = record
            elif record[3] > cur[3]:
                cur = list(record)
        self.db.commit()
        bquery.close()
        insert.close()
        agn.log(
            agn.LV_INFO, 'collect',
            'Read %d records (%d uniques) and inserted %d' %
            (records, uniques, inserts))
Esempio n. 4
0
	def collectNewBounces (self): #{{{
		agn.log (agn.LV_INFO, 'collect', 'Start collecting new bounces')

		iquery = 'INSERT INTO bounce_collect_tbl (customer_id, company_id, mailing_id, change_date) VALUES (:customer, :company, :mailing, current_timestamp)'
		insert = self.db.cursor ()
		if insert is None:
			raise agn.error ('collectNewBounces: Failed to get new cursor for insertion')
		bquery = self.db.cursor ()
		if bquery is None:
			raise agn.error ('collectNewBounces: Failed to get new cursor for bounce query')
		data = {}
		query =  'SELECT customer_id, company_id, mailing_id, detail FROM bounce_tbl WHERE %s ORDER BY company_id, customer_id' % self.timestamp.makeBetweenClause ('change_date', data)
		cur = [0, 0, 0, 0]
		(records, uniques, inserts) = (0, 0, 0)
		if bquery.query (query, data) is None:
			raise agn.error ('collectNewBounces: Failed to query bounce_tbl using: %s' % query)
		while cur is not None:
			try:
				record = bquery.next ()
				records += 1
			except StopIteration:
				record = None
			if record is None or cur[0] != record[0] or cur[1] != record[1]:
				if record is not None:
					uniques += 1
				if cur[0] > 0 and cur[3] >= 400 and cur[3] < 510:
					parm = {
						'customer': cur[0],
						'company': cur[1],
						'mailing': cur[2]
					}
					insert.update (iquery, parm)
					inserts += 1
					if inserts % 10000 == 0 or record is None:
						agn.log (agn.LV_DEBUG, 'collect', 'Inserted now %s record%s' % (agn.numfmt (inserts), exts (inserts)))
						insert.sync ()
				cur = record
			elif record[3] > cur[3]:
				cur = list (record)
		self.db.commit ()
		bquery.close ()
		insert.close ()
		agn.log (agn.LV_INFO, 'collect', 'Read %d records (%d uniques) and inserted %d' % (records, uniques, inserts))
Esempio n. 5
0
            return False
        try:
            fd = open (tfname, 'r')
        except IOError, e:
            agn.log (agn.LV_ERROR, 'update', 'Unable to open %s: %s' % (tfname, `e.args`))
            fd = None
        if fd is None:
            return False
        self.lineno = 0
        removeTemp = True
        rc = self.updateStart (inst)

        for line in [agn.chop (l) for l in fd.readlines ()]:
            self.lineno += 1
            if self.lineno % 10000 == 0:
                agn.log (agn.LV_INFO, 'update', '%s: Now at line %s' % (self.name, agn.numfmt (self.lineno)))
            if not self.updateLine (inst, line):
                if not self.saveToFail (line):
                    removeTemp = False
                rc = False
            else:
                if not self.saveToLog (line):
                    removeTemp = False
        if not self.updateEnd (inst):
            rc = False
        fd.close ()
        if removeTemp:
            self.__removeFile (tfname)
        inst.sync ()
        return rc
Esempio n. 6
0
        except IOError, e:
            agn.log(agn.LV_ERROR, 'update',
                    'Unable to open %s: %s' % (tfname, ` e.args `))
            fd = None
        if fd is None:
            return False
        self.lineno = 0
        removeTemp = True
        rc = self.updateStart(inst)

        for line in [agn.chop(l) for l in fd.readlines()]:
            self.lineno += 1
            if self.lineno % 10000 == 0:
                agn.log(
                    agn.LV_INFO, 'update', '%s: Now at line %s' %
                    (self.name, agn.numfmt(self.lineno)))
            if not self.updateLine(inst, line):
                if not self.saveToFail(line):
                    removeTemp = False
                rc = False
            else:
                if not self.saveToLog(line):
                    removeTemp = False
        if not self.updateEnd(inst):
            rc = False
        fd.close()
        if removeTemp:
            self.__removeFile(tfname)
        inst.sync()
        return rc
Esempio n. 7
0
    def convertToHardbounce(self):  #{{{
        agn.log(agn.LV_INFO, 'conv',
                'Start converting softbounces to hardbounce')

        coll = [1]
        stats = []
        for company in sorted(coll):
            cstat = [company, 0, 0]
            stats.append(cstat)
            agn.log(agn.LV_INFO, 'conv', 'Working on %d' % company)
            dquery = 'DELETE FROM softbounce_email_tbl WHERE company_id = %d AND email = :email' % company
            dcurs = self.db.cursor()
            uquery = self.curs.rselect(
                'UPDATE customer_%d_binding_tbl SET user_status = 2, user_remark = \'Softbounce\', exit_mailing_id = :mailing, change_date = %%(sysdate)s WHERE customer_id = :customer AND user_status = 1'
                % company)
            bquery = self.curs.rselect(
                'INSERT INTO bounce_tbl (company_id, customer_id, detail, mailing_id, change_date, dsn) VALUES (%d, :customer, 510, :mailing, %%(sysdate)s, 599)'
                % company)
            ucurs = self.db.cursor()

            squery = 'SELECT email, mailing_id, bnccnt, creation_date, change_date FROM softbounce_email_tbl WHERE company_id = %d AND bnccnt > 7 AND DATEDIFF(change_date,creation_date) > 30' % company
            scurs = self.db.cursor()
            if None in [dcurs, ucurs, scurs]:
                raise agn.error('Failed to setup curses')

            lastClick = 30
            lastOpen = 30

            def toDatetime(offset):
                tm = time.localtime(time.time() - offset * 24 * 60 * 60)
                return datetime.datetime(tm.tm_year, tm.tm_mon, tm.tm_mday)

            lastClickTS = toDatetime(lastClick)
            lastOpenTS = toDatetime(lastOpen)
            ccount = 0
            for record in scurs.query(squery):
                parm = {
                    'email': record[0],
                    'mailing': record[1],
                    'bouncecount': record[2],
                    'creationdate': record[3],
                    'timestamp': record[4],
                    'customer': None
                }
                query = 'SELECT customer_id FROM customer_%d_tbl WHERE email = :email ' % company
                data = self.curs.querys(query, parm, cleanup=True)
                if data is None:
                    continue
                custs = [
                    agn.struct(id=_d, click=0, open=0) for _d in data if _d
                ]
                if not custs:
                    continue
                if len(custs) == 1:
                    cclause = 'customer_id = %d' % custs[0].id
                else:
                    cclause = 'customer_id IN (%s)' % ', '.join(
                        [str(_c.id) for _c in custs])

                parm['ts'] = lastClickTS
                query = 'SELECT customer_id, count(*) FROM rdir_log_tbl WHERE %s AND company_id = %d' % (
                    cclause, company)
                query += ' AND change_date > :ts GROUP BY customer_id'
                for r in self.curs.queryc(query, parm, cleanup=True):
                    for c in custs:
                        if c.id == r[0]:
                            c.click += r[1]
                parm['ts'] = lastOpenTS
                query = 'SELECT customer_id, count(*) FROM onepixel_log_tbl WHERE %s AND company_id = %d' % (
                    cclause, company)
                query += ' AND change_date > :ts GROUP BY customer_id'
                for r in self.curs.queryc(query, parm, cleanup=True):
                    for c in custs:
                        if c.id == r[0]:
                            c.open += r[1]
                for c in custs:
                    if c.click > 0 or c.open > 0:
                        cstat[1] += 1
                        agn.log(
                            agn.LV_INFO, 'conv',
                            'Email %s [%d] has %d klick(s) and %d onepix(es) -> active'
                            % (parm['email'], c.id, c.click, c.open))
                    else:
                        cstat[2] += 1
                        agn.log(
                            agn.LV_INFO, 'conv',
                            'Email %s [%d] has no klicks and no onepixes -> hardbounce'
                            % (parm['email'], c.id))
                        parm['customer'] = c.id
                        ucurs.update(uquery, parm, cleanup=True)
                        ucurs.execute(bquery, parm, cleanup=True)
                dcurs.update(dquery, parm, cleanup=True)
                ccount += 1
                if ccount % 1000 == 0:
                    agn.log(agn.LV_INFO, 'conv',
                            'Commiting at %s' % agn.numfmt(ccount))
                    self.db.commit()
            self.db.commit()
            scurs.close()
            ucurs.close()
            dcurs.close()
        for cstat in stats:
            agn.log(
                agn.LV_INFO, 'conv',
                'Company %d has %d active and %d marked as hardbounced users' %
                tuple(cstat))
        agn.log(agn.LV_INFO, 'conv',
                'Converting softbounces to hardbounce done')
Esempio n. 8
0
    def convertToHardbounce(self):  #{{{
        agn.log(agn.LV_INFO, 'conv',
                'Start converting softbounces to hardbounce')
        coll = self.__ccoll(
            'SELECT distinct company_id FROM softbounce_email_tbl WHERE company_id IN (SELECT company_id FROM company_tbl WHERE status = \'active\')',
            None, None, None)
        stats = []
        for company in sorted(coll):
            cstat = [company, 0, 0]
            stats.append(cstat)
            agn.log(agn.LV_INFO, 'conv', 'Working on %d' % company)
            dquery = 'DELETE FROM softbounce_email_tbl WHERE company_id = %d AND email = :email' % company
            dcurs = self.db.cursor()
            uquery = self.curs.rselect(
                'UPDATE customer_%d_binding_tbl SET user_status = 2, user_remark = \'bounce:soft\', exit_mailing_id = :mailing, timestamp = %%(sysdate)s WHERE customer_id = :customer AND user_status = 1'
                % company)
            bquery = self.curs.rselect(
                'INSERT INTO bounce_tbl (company_id, customer_id, detail, mailing_id, timestamp, dsn) VALUES (%d, :customer, 510, :mailing, %%(sysdate)s, 599)'
                % company)
            ucurs = self.db.cursor()
            squery = 'SELECT email, mailing_id, bnccnt, creation_date, timestamp FROM softbounce_email_tbl '
            bnccnt = self.__cfg(company, 'convert-bounce-count', 40)
            daydiff = self.__cfg(company, 'convert-bounce-duration', 30)
            squery += self.curs.qselect(
                oracle=
                'WHERE company_id = %d AND bnccnt > %d AND timestamp-creation_date > %d'
                % (company, bnccnt, daydiff),
                mysql=
                'WHERE company_id = %d AND bnccnt > %d AND DATEDIFF(timestamp,creation_date) > %d'
                % (company, bnccnt, daydiff))
            scurs = self.db.cursor()
            if None in [dcurs, ucurs, scurs]:
                raise agn.error('Failed to setup curses')
            lastClick = self.__cfg(company, 'last-click', 30)
            lastOpen = self.__cfg(company, 'last-open', 30)

            def toDatetime(offset):
                tm = time.localtime(time.time() - offset * 24 * 60 * 60)
                return datetime.datetime(tm.tm_year, tm.tm_mon, tm.tm_mday)

            lastClickTS = toDatetime(lastClick)
            lastOpenTS = toDatetime(lastOpen)
            if Cache is not None:
                rcache = Cache(1000)
                ocache = Cache(1000 * 1000)
                ccurs = self.db.cursor()
                if None in [rcache, ocache, ccurs
                            ] or not rcache.valid() or not ocache.valid():
                    raise agn.error('Failed to setup caching')
                agn.log(agn.LV_INFO, 'cache',
                        'Setup rdir log cache for %d' % company)
                query = 'SELECT customer_id FROM rdirlog_%d_tbl WHERE timestamp > :ts' % company
                parm = {'ts': lastClickTS}
                for record in ccurs.query(query, parm):
                    rcache.add(record[0])
                agn.log(agn.LV_INFO, 'cache',
                        'Setup one pixel log cache for %d' % company)
                query = 'SELECT customer_id FROM onepixellog_%d_tbl WHERE timestamp > :ts' % company
                parm = {'ts': lastOpenTS}
                for record in ccurs.query(query, parm):
                    ocache.add(record[0])
                ccurs.close()
                agn.log(agn.LV_INFO, 'cache', 'Setup completed')
            ccount = 0
            for record in scurs.query(squery):
                parm = {
                    'email': record[0],
                    'mailing': record[1],
                    'bouncecount': record[2],
                    'creationdate': record[3],
                    'timestamp': record[4],
                    'customer': None
                }
                query = 'SELECT customer_id FROM customer_%d_tbl WHERE email = :email ' % company
                data = self.curs.querys(query, parm, cleanup=True)
                if data is None:
                    continue
                custs = [
                    agn.mutable(id=_d, click=0, open=0) for _d in data if _d
                ]
                if not custs:
                    continue
                if len(custs) == 1:
                    cclause = 'customer_id = %d' % custs[0].id
                else:
                    cclause = 'customer_id IN (%s)' % ', '.join(
                        [str(_c.id) for _c in custs])
                if Cache is not None:
                    for c in custs:
                        c.click += rcache.get(c.id, 0)
                        c.open += ocache.get(c.id, 0)
                else:
                    parm['ts'] = lastClickTS
                    query = 'SELECT customer_id, count(*) FROM rdirlog_%d_tbl WHERE %s AND timestamp > :ts GROUP BY customer_id' % (
                        company, cclause)
                    for r in self.curs.queryc(query, parm, cleanup=True):
                        for c in custs:
                            if c.id == r[0]:
                                c.click += r[1]
                    parm['ts'] = lastOpenTS
                    query = 'SELECT customer_id, count(*) FROM onepixellog_%d_tbl WHERE %s AND timestamp > :ts GROUP BY customer_id' % (
                        company, cclause)
                    for r in self.curs.queryc(query, parm, cleanup=True):
                        for c in custs:
                            if c.id == r[0]:
                                c.open += r[1]
                for c in custs:
                    if c.click > 0 or c.open > 0:
                        cstat[1] += 1
                        agn.log(
                            agn.LV_INFO, 'conv',
                            'Email %s [%d] has %d klick(s) and %d onepix(es) -> active'
                            % (parm['email'], c.id, c.click, c.open))
                    else:
                        cstat[2] += 1
                        agn.log(
                            agn.LV_INFO, 'conv',
                            'Email %s [%d] has no klicks and no onepixes -> hardbounce'
                            % (parm['email'], c.id))
                        parm['customer'] = c.id
                        ucurs.update(uquery, parm, cleanup=True)
                        ucurs.execute(bquery, parm, cleanup=True)
                dcurs.update(dquery, parm, cleanup=True)
                ccount += 1
                if ccount % 1000 == 0:
                    agn.log(agn.LV_INFO, 'conv',
                            'Commiting at %s' % agn.numfmt(ccount))
                    self.db.commit()
            if Cache is not None:
                rcache.done()
                ocache.done()
            self.db.commit()
            scurs.close()
            ucurs.close()
            dcurs.close()
        for cstat in stats:
            agn.log(
                agn.LV_INFO, 'conv',
                'Company %d has %d active and %d marked as hardbounced users' %
                tuple(cstat))
        agn.log(agn.LV_INFO, 'conv',
                'Converting softbounces to hardbounce done')
Esempio n. 9
0
    def collectNewBounces(self):  #{{{
        agn.log(agn.LV_INFO, 'collect', 'Start collecting new bounces')
        cursor = self.db.cursor()
        if cursor is None:
            raise agn.error(
                'collectNewBounces: Failed to get new cursor for collecting')
        #
        Update = collections.namedtuple(
            'Update', ['customer_id', 'company_id', 'mailing_id', 'detail'])
        data = {}
        query = 'SELECT customer_id, company_id, mailing_id, detail FROM bounce_tbl WHERE %s ORDER BY company_id, customer_id' % self.timestamp.makeBetweenClause(
            'timestamp', data)

        class Collect(agn.Stream.Collector):
            def supplier(self):
                self.data = {}
                self.uniques = 0

            def accumulator(self, supplier, element):
                update = Update(*element)
                if update.detail >= 400 and update.detail < 520:
                    key = (update.company_id, update.customer_id)
                    try:
                        if update.detail > self.data[key].detail:
                            self.data[key] = update
                    except KeyError:
                        self.data[key] = update
                        self.uniques += 1

            def finisher(self, supplier, count):
                return (count, self.uniques, self.data)

        (records, uniques, updates) = cursor.stream(query,
                                                    data).collect(Collect())
        agn.log(
            agn.LV_INFO, 'collect',
            'Read %s records (%s uniques) and have %s for insert' %
            (agn.numfmt(records), agn.numfmt(uniques), agn.numfmt(
                len(updates))))
        #
        inserts = 0
        query = self.curs.rselect(
            'INSERT INTO bounce_collect_tbl (customer_id, company_id, mailing_id, timestamp) VALUES (:customer, :company, :mailing, %(sysdate)s)'
        )
        for update in (agn.Stream(updates.itervalues()).sorted()):
            cursor.update(
                query, {
                    'customer': update.customer_id,
                    'company': update.company_id,
                    'mailing': update.mailing_id
                })
            inserts += 1
            if inserts % 10000 == 0:
                cursor.sync()
        agn.log(
            agn.LV_INFO, 'collect',
            'Inserted %s records into bounce_collect_tbl' %
            agn.numfmt(inserts))
        cursor.sync()
        cursor.close()
        #
        agn.log(
            agn.LV_INFO, 'collect',
            'Read %d records (%d uniques) and inserted %d' %
            (records, uniques, inserts))
        companyIDs = []
        query = 'SELECT distinct company_id FROM bounce_collect_tbl'
        for record in self.curs.query(query):
            if record[0] is not None and record[0] > 0:
                companyIDs.append(record[0])
        agn.log(
            agn.LV_INFO, 'collect',
            'Remove active receivers from being watched for %d compan%s' %
            (len(companyIDs), exty(len(companyIDs))))
        allCount = 0
        for companyID in companyIDs:
            table = 'success_%d_tbl' % companyID
            if table in self.tables:
                data = {'companyID': companyID}
                query = self.curs.qselect (
                 oracle = 'DELETE FROM bounce_collect_tbl mail WHERE EXISTS (SELECT 1 FROM %s su WHERE ' % table + \
                   'mail.customer_id = su.customer_id AND mail.company_id = :companyID AND %s)' % self.timestamp.makeBetweenClause ('timestamp', data),
                 mysql = 'DELETE mail.* FROM bounce_collect_tbl mail, %s su WHERE ' % table + \
                  'mail.customer_id = su.customer_id AND mail.company_id = :companyID AND %s' % self.timestamp.makeBetweenClause ('su.timestamp', data)
                )
                count = self.curs.execute(query, data, commit=True)
                agn.log(
                    agn.LV_INFO, 'collect',
                    'Removed %s active receiver%s for companyID %d' %
                    (agn.numfmt(count), exts(count), companyID))
                allCount += count
            else:
                agn.log(
                    agn.LV_INFO, 'collect',
                    'Skip removing active receivers for companyID %d due to missing table %s'
                    % (companyID, table))
        agn.log(
            agn.LV_INFO, 'collect', 'Finished removing %s receiver%s' %
            (agn.numfmt(allCount), exts(allCount)))
Esempio n. 10
0
	def convertToHardbounce (self): #{{{
		agn.log (agn.LV_INFO, 'conv', 'Start converting softbounces to hardbounce')

		coll = [1]
		stats = []
		for company in sorted (coll):
			cstat = [company, 0, 0]
			stats.append (cstat)
			agn.log (agn.LV_INFO, 'conv', 'Working on %d' % company)
			dquery = 'DELETE FROM softbounce_email_tbl WHERE company_id = %d AND email = :email' % company
			dcurs = self.db.cursor ()
			uquery = self.curs.rselect ('UPDATE customer_%d_binding_tbl SET user_status = 2, user_remark = \'Softbounce\', exit_mailing_id = :mailing, change_date = %%(sysdate)s WHERE customer_id = :customer AND user_status = 1' % company)
			bquery = self.curs.rselect ('INSERT INTO bounce_tbl (company_id, customer_id, detail, mailing_id, change_date, dsn) VALUES (%d, :customer, 510, :mailing, %%(sysdate)s, 599)' % company)
			ucurs = self.db.cursor ()

			squery =  'SELECT email, mailing_id, bnccnt, creation_date, change_date FROM softbounce_email_tbl WHERE company_id = %d AND bnccnt > 7 AND DATEDIFF(change_date,creation_date) > 30' % company
			scurs = self.db.cursor ()
			if None in [dcurs, ucurs, scurs]:
				raise agn.error ('Failed to setup curses')

			lastClick = 30
			lastOpen = 30
			def toDatetime (offset):
				tm = time.localtime (time.time () -offset * 24 * 60 * 60)
				return datetime.datetime (tm.tm_year, tm.tm_mon, tm.tm_mday)
			lastClickTS = toDatetime (lastClick)
			lastOpenTS = toDatetime (lastOpen)
			ccount = 0
			for record in scurs.query (squery):
				parm = {
					'email': record[0],
					'mailing': record[1],
					'bouncecount': record[2],
					'creationdate': record[3],
					'timestamp': record[4],
					'customer': None
				}
				query =  'SELECT customer_id FROM customer_%d_tbl WHERE email = :email ' % company
				data = self.curs.querys (query, parm, cleanup = True)
				if data is None:
					continue
				custs = [agn.struct (id = _d, click = 0, open = 0) for _d in data if _d]
				if not custs:
					continue
				if len (custs) == 1:
					cclause = 'customer_id = %d' % custs[0].id
				else:
					cclause = 'customer_id IN (%s)' % ', '.join ([str (_c.id) for _c in custs])

				parm['ts'] = lastClickTS
				query =  'SELECT customer_id, count(*) FROM rdir_log_tbl WHERE %s AND company_id = %d' % (cclause, company)
				query += ' AND change_date > :ts GROUP BY customer_id'
				for r in self.curs.queryc (query, parm, cleanup = True):
					for c in custs:
						if c.id == r[0]:
							c.click += r[1]
				parm['ts'] = lastOpenTS
				query =  'SELECT customer_id, count(*) FROM onepixel_log_tbl WHERE %s AND company_id = %d' % (cclause, company)
				query += ' AND change_date > :ts GROUP BY customer_id'
				for r in self.curs.queryc (query, parm, cleanup = True):
					for c in custs:
						if c.id == r[0]:
							c.open += r[1]
				for c in custs:
					if c.click > 0 or c.open > 0:
						cstat[1] += 1
						agn.log (agn.LV_INFO, 'conv', 'Email %s [%d] has %d klick(s) and %d onepix(es) -> active' % (parm['email'], c.id, c.click, c.open))
					else:
						cstat[2] += 1
						agn.log (agn.LV_INFO, 'conv', 'Email %s [%d] has no klicks and no onepixes -> hardbounce' % (parm['email'], c.id))
						parm['customer'] = c.id
						ucurs.update (uquery, parm, cleanup = True)
						ucurs.execute (bquery, parm, cleanup = True)
				dcurs.update (dquery, parm, cleanup = True)
				ccount += 1
				if ccount % 1000 == 0:
					agn.log (agn.LV_INFO, 'conv', 'Commiting at %s' % agn.numfmt (ccount))
					self.db.commit ()
			self.db.commit ()
			scurs.close ()
			ucurs.close ()
			dcurs.close ()
		for cstat in stats:
			agn.log (agn.LV_INFO, 'conv', 'Company %d has %d active and %d marked as hardbounced users' % tuple (cstat))
		agn.log (agn.LV_INFO, 'conv', 'Converting softbounces to hardbounce done')