def test_read_table(): database = Database() df = database.read_table('event_aggregates', limit=10) assert len(df) == 10 df = database.read_table( 'event_aggregates', query=('name', 'Rodef 2100'), where=[('start_datetime',{'>=': "'2018-01-01'"})], limit=10 ) assert len(df) > 0 for i in df.index: row = df.loc[i] assert str(row['start_datetime']) >= '2018-01-01' assert '2100' in row['name'] or 'rodef' in row['name'].lower() count = database.count_rows('event_aggregates', query=('name', 'Rodef 2100')) assert count > 0
class Network(): """ Pulls in data from the events database and performs social network analysis. """ def __init__(self): daiquiri.setup(level=logging.INFO) self.logger = daiquiri.getLogger(__name__) self.database = Database() self.events_manager = Events() self.network = None self.metrics = { 'node_connectivity': self.node_connectivity, 'edge_connectivity': self.edge_connectivity, 'density': self.density, 'membership_scaled_density': self.membership_scaled_density } def get_events(self, start, end): """ Pulls events from the event database within the specified date range. """ events = self.events_manager.database.read_table( 'event_aggregates', columns=['id', 'start_datetime'], where=[('start_datetime', { '>=': start, '<': end })], ) return events def evaluate_network(self, metrics=None, date=None): """ Constructs a co-attendance network ending at the specified data and begging <lag> days before the date. """ if not metrics: metrics = list(self.metrics.keys()) elif isinstance(metrics, str): metrics = [metrics] biggest_subgraph = self.biggest_subgraph(self.network) evaluation = {} for metric in metrics: if metric in self.metrics: if metric.endswith('connectivity'): value = self.metrics[metric](biggest_subgraph) else: value = self.metrics[metric](self.network) evaluation[metric] = value return evaluation def build_network(self, date, lag, max_attendees=None): """ Builds the congregational co-attendance network ending at the specified date and ending <lag> days later Parameters ---------- date: the start date in 'YYYY-MM-DD' format lag: int, the number of days to go back max_attendees: int, ignores events that have more than the max number of attendees Returns ------- sets the network attribute to be a network x graph """ network = nx.Graph() # Determine the start and end dates for the pull self.date = date split_date = date.split('-') year = int(split_date[0]) month = int(split_date[1]) day = int(split_date[2]) end_datetime = datetime.datetime(year, month, day) end = "'{}'".format(date) start_datetime = end_datetime - datetime.timedelta(days=lag) start = "'{}'".format(str(start_datetime)[:10]) # Build the network G(V,E) where V is the set of participants # and an edge exists between two vertices if they have # attended the same events events = self.get_events(start, end) msg = '\nStart: {date} \nLag: {lag} days \nEvent Count: {count}' msg = msg.format(date=date, lag=lag, count=len(events)) self.logger.info(msg) for event_id in events['id']: attendees = self.events_manager.get_attendees(event_id) if max_attendees: if len(attendees) > max_attendees: continue attendees = [x for x in attendees if x['first_name']] names = [ ' '.join([x['first_name'], x['last_name']]).lower() for x in attendees ] names = list(set(names)) # Drops duplicates pairs = itertools.combinations(names, 2) for pair in pairs: network.add_edge(*pair) self.network = network @staticmethod def density(network): """ Finds the density of the graph. """ return nx.density(network) * 100 def membership_scaled_density(self, network): """ Finds the density of the network scaled by the number of members. """ date = "'{}'".format(self.date) member_count = self.database.count_rows('members', where=[('membership_date', { '<=': date })]) actual_connections = len(network.edges) potential_connections = (member_count * (member_count - 1)) / 2 density = actual_connections / potential_connections return float(density) * 100 @staticmethod def node_connectivity(network): """ Computes the node connectivity for the network. """ connectivity = approx.node_connectivity(network) return connectivity @staticmethod def edge_connectivity(network): """ Compute the edge connectivity for the network. """ connectivity = nx.connectivity.edge_connectivity(network) return connectivity @staticmethod def biggest_subgraph(network): """ Finds the biggest fully connected subgraph of the network. """ connected_components = nx.connected_components(network) graphs = [network.subgraph(x).copy() for x in connected_components] sizes = [len(x.nodes) for x in graphs] idx = sizes.index(max(sizes)) biggest_graph = graphs[idx] return biggest_graph