-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from Worvast/master
New version 3
- Loading branch information
Showing
52 changed files
with
1,327 additions
and
471 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
__description__ = 'Devo Python Library.' | ||
__url__ = 'http://www.devo.com' | ||
__version__ = "2.0.1" | ||
__version__ = "3.0.0" | ||
__author__ = 'Devo' | ||
__author_email__ = '[email protected]' | ||
__license__ = 'MIT' | ||
__copyright__ = 'Copyright 2019 Devo' | ||
__copyright__ = 'Copyright 2020 Devo' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
from .senders.batch_sender import BatchSender | ||
from .senders.file_sender import FileSender | ||
from .senders.syslog_sender import SyslogSender | ||
from .senders.syslog_raw_sender import SyslogRawSender | ||
from .senders.template_parser import TemplateParser | ||
from .generators.batch_fake_generator import BatchFakeGenerator | ||
from .generators.file_fake_generator import FileFakeGenerator | ||
from .generators.syslog_fake_generator import SyslogFakeGenerator | ||
from .generators.syslog_raw_fake_generator import SyslogRawFakeGenerator | ||
from .generators.template_parser import TemplateParser | ||
from .generators.simulation_fake_generator import SimulationFakeGenerator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from .batch_fake_generator import BatchFakeGenerator | ||
from .file_fake_generator import FileFakeGenerator | ||
from .syslog_fake_generator import SyslogFakeGenerator | ||
from .syslog_raw_fake_generator import SyslogRawFakeGenerator | ||
from .template_parser import TemplateParser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Base Sender, father of all other providers""" | ||
import random | ||
import threading | ||
import time | ||
from datetime import datetime, timedelta | ||
from pycron import is_now | ||
from .template_parser import TemplateParser | ||
|
||
|
||
class BaseFakeGenerator(threading.Thread): | ||
"""Base provider main class""" | ||
|
||
def __init__(self, engine=None, template=None, **kwargs): | ||
""" | ||
Init BaseFakeGenerator, all generator use this class as father | ||
:param engine (Sender): Sender object, if neccesary, for send data | ||
:param template (str, bytes): Template, read, in a variable, not the | ||
path to a file | ||
:param kwargs: List of accepted variables below | ||
:param time_rules (dict): Time rules as crondates format for | ||
variable frequeny and probability | ||
:param frequency (list): Fixed shipping frequency list (min, max) | ||
in seconds | ||
:param probability (int): Fixed shipping probability | ||
from 0 to 100(%) | ||
:param interactive (bool): Interactive mode | ||
:param simulation (bool): Simulation mode, not send/write if true | ||
:param verbose (bool): Verbose mode | ||
:param date_generator (object): Alternative date generator | ||
for templates | ||
:param providers (dict): Custom providers dict | ||
""" | ||
|
||
threading.Thread.__init__(self) | ||
self.engine = engine | ||
|
||
if kwargs.get('time_rules', None) is not None: | ||
self.time_rules = kwargs.get("time_rules") | ||
self.set_last_time_rule() | ||
|
||
self.cron_child = threading.Thread( | ||
target=self.check_time_rules | ||
) | ||
self.cron_child.setDaemon(True) | ||
self.cron_child.start() | ||
else: | ||
self.prob = kwargs.get('probability', 100) | ||
self.freq = kwargs.get('frequency', (1, 1)) | ||
|
||
self.interactive = kwargs.get('interactive', False) | ||
self.simulation = kwargs.get('simulation', False) | ||
self.verbose = kwargs.get('verbose', False) | ||
self.parser = TemplateParser(template=str(template), | ||
providers=kwargs.get('providers', {}), | ||
date_generator= | ||
kwargs.get('date_generator', None)) | ||
|
||
def process(self, date_generator=None, **kwargs): | ||
"""Process template""" | ||
return self.parser.process(date_generator=date_generator, | ||
**kwargs) | ||
|
||
def wait(self): | ||
"""Time to wait between events""" | ||
# freq[0] is the minimum | ||
# freq[1] is the maximum | ||
if self.freq[0] == self.freq[1]: | ||
secs = self.freq[0] | ||
elif self.freq[1] < self.freq[0]: | ||
secs = random.uniform(self.freq[1], self.freq[0]) | ||
else: | ||
secs = random.uniform(self.freq[0], self.freq[1]) | ||
time.sleep(secs) | ||
|
||
def set_last_time_rule(self): | ||
""" | ||
When crondate time rules are used, it is found which one should have | ||
been executed last and the probability and frequency are set | ||
:return None | ||
""" | ||
now = datetime.now() | ||
try: | ||
for _ in range(0, 525600): | ||
for item in self.time_rules: | ||
if is_now(item["rule"], now): | ||
self.prob = item['probability'] | ||
self.freq = item['frequency'] | ||
raise Exception('found') | ||
now = now - timedelta(minutes=1) | ||
except Exception as inst: | ||
if inst.args[0] != "found": | ||
raise Exception(inst) | ||
|
||
def check_time_rules(self): | ||
""" | ||
Controls every minute if a change in frequency and probability | ||
is to be made based on the specified rules | ||
:return: | ||
""" | ||
while True: | ||
for item in self.time_rules: | ||
if is_now(item["rule"]): | ||
self.prob = item['probability'] | ||
self.freq = item['frequency'] | ||
break | ||
time.sleep(60) | ||
|
||
def probability(self): | ||
"""Calculate probability""" | ||
k = random.randint(0, 100) | ||
if k <= int(self.prob): | ||
return True | ||
return False | ||
|
||
def run(self): | ||
"""Run example (for override)""" | ||
while True: | ||
if self.probability(): | ||
# Do something | ||
pass | ||
self.wait() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
"""Batch sender provider""" | ||
import datetime | ||
import os.path | ||
import random | ||
import sys | ||
import click | ||
|
||
from .base_fake_generator import BaseFakeGenerator | ||
|
||
|
||
class BatchFakeGenerator(BaseFakeGenerator): | ||
""" | ||
Generates a lot of events in a single batch without any waits, it | ||
supports generating events from the past | ||
""" | ||
|
||
GENERATION_ENDED_TOKEN = '###END###' | ||
|
||
def __init__(self, template=None, start_date=None, end_date=None, | ||
**kwargs): | ||
|
||
BaseFakeGenerator.__init__(self, template=template, **kwargs) | ||
|
||
self._start_date = start_date | ||
self._end_date = end_date | ||
self.date_format = kwargs.get('date_format', "%Y-%m-%d %H:%M:%S.%f") | ||
self.dont_remove_microseconds = kwargs.get('dont_remove_microseconds', | ||
False) | ||
self.file_name = kwargs.get("file_name") if kwargs.get("file_name") \ | ||
else "eventbatch.log" | ||
|
||
@staticmethod | ||
def date_range(start_date=None, end_date=None, frequency=None, | ||
probability=None, | ||
date_format="%Y-%m-%d %H:%M:%S.%f", | ||
dont_remove_microseconds=True): | ||
""" | ||
Generates date range | ||
:param start_date:me objects between a start date and an end date with | ||
some given frequency, | ||
Some events can be piled up in the same millisecond, this happens | ||
sometimes with real data. | ||
:param end_date: | ||
:param start_date: | ||
:param frequency: | ||
:param probability: | ||
:param date_format: | ||
:param dont_remove_microseconds: | ||
:return: | ||
""" | ||
|
||
# Control how fast events are spaced between them with a | ||
# frequency (--frequency argument) | ||
millis = 0 | ||
idx = 0 | ||
while True: | ||
|
||
millis_increment = random.uniform(frequency[0], | ||
frequency[1]) * 1000 | ||
|
||
# Add some randomness to the event generation with | ||
# the --probability argument | ||
k = random.randint(0, 100) | ||
if k <= int(probability): | ||
millis += millis_increment | ||
|
||
idx += 1 | ||
next_date = start_date + datetime.timedelta(milliseconds=millis) | ||
|
||
if next_date > end_date: | ||
yield BatchFakeGenerator.GENERATION_ENDED_TOKEN | ||
else: | ||
if dont_remove_microseconds: | ||
yield next_date.strftime(date_format) | ||
else: | ||
yield next_date.strftime(date_format)[:-3] | ||
|
||
def run(self): | ||
"""Run function for cli or call function""" | ||
if os.path.exists(self.file_name): | ||
raise ValueError('an {} file already exists, remove it before' | ||
' generating new events'.format(self.file_name)) | ||
|
||
counter = 0 | ||
date_generator = self.date_range( | ||
self._start_date, self._end_date, self.freq, self.prob, | ||
self.date_format, self.dont_remove_microseconds) | ||
with open(self.file_name, "w") as f: | ||
while True: | ||
lines = self.process(date_generator=date_generator).split('\n') | ||
for line in lines: | ||
if BatchFakeGenerator.GENERATION_ENDED_TOKEN in line: | ||
print("Wrote {} events in {}".format(counter, | ||
self.file_name), | ||
file=sys.stdout) | ||
return | ||
if counter % 100 == 0: | ||
click.echo('Wrote {} lines'.format(counter), | ||
file=sys.stderr) | ||
click.echo(line[:40], file=sys.stderr) | ||
if self.verbose: | ||
print(line, file=sys.stdout) | ||
f.write(line+"\n") | ||
counter += 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# -*- coding: utf-8 -*- | ||
"""File Sender provider""" | ||
from datetime import datetime | ||
from .realtime_fake_generator import RealtimeFakeGenerator | ||
|
||
|
||
class FileFakeGenerator(RealtimeFakeGenerator): | ||
"""Generate a lot of events from file""" | ||
def __init__(self, template=None, **kwargs): | ||
RealtimeFakeGenerator.__init__(self, template=template, **kwargs) | ||
self.last_flush = int(datetime.now().timestamp()) | ||
self.file_name = kwargs.get('file_name', "safestream.log") | ||
self.file = None | ||
|
||
def write_row(self, message=None): | ||
"""Write row to file""" | ||
self.file.write(message) | ||
self.file.write('\n') | ||
now = int(datetime.now().timestamp()) | ||
# flush every 5 secs | ||
if now - self.last_flush > 5: | ||
self.last_flush = now | ||
self.file.flush() | ||
|
||
def run(self): | ||
"""Run function for cli or call function""" | ||
with open(self.file_name, "a") as self.file: | ||
self.realtime_iteration(write_function=self.write_row) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# -*- coding: utf-8 -*- | ||
"""realtime base sender provider""" | ||
from datetime import datetime | ||
from .base_fake_generator import BaseFakeGenerator | ||
|
||
|
||
class RealtimeFakeGenerator(BaseFakeGenerator): | ||
"""Realtime fake data generation""" | ||
def __init__(self, engine=None, template=None, **kwargs): | ||
"""Realtime fake data generation""" | ||
BaseFakeGenerator.__init__(self, engine=engine, template=template, | ||
**kwargs) | ||
|
||
def realtime_iteration(self, write_function=None): | ||
"""Realtime function for parse and send/show generated data""" | ||
while True: | ||
lines = self.process().split('\n') | ||
for line in lines: | ||
if self.probability(): | ||
if not self.simulation: | ||
write_function(line) | ||
now = datetime.utcnow().ctime() | ||
if self.verbose: | ||
print('{0} => {1}'.format(now, str(line))) | ||
else: | ||
now = datetime.utcnow().ctime() | ||
if self.verbose: | ||
print('{0} => Skipped by prob.'.format(now)) | ||
|
||
if self.interactive: | ||
input("» Press Enter for next iteration «") | ||
else: | ||
self.wait() |
Oops, something went wrong.