Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,9 @@ ENV/
.idea/

cache/

# Internal project notes (not for distribution)
docs/internal/

# Local workspace / scratch
workspace/
3 changes: 2 additions & 1 deletion graphfaker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
__version__ = "0.2.0"

from .core import GraphFaker
from .fetchers.trust import TrustGraphFetcher
from .fetchers.wiki import WikiFetcher
from .logger import configure_logging, logger

__all__ = ["GraphFaker", "logger", "configure_logging", "add_file_logging"]
__all__ = ["GraphFaker", "TrustGraphFetcher", "logger", "configure_logging", "add_file_logging"]
50 changes: 47 additions & 3 deletions graphfaker/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
Command-line interface for GraphFaker.
"""

from venv import logger
import os

import typer

from graphfaker.core import GraphFaker
from graphfaker.enums import FetcherType
from graphfaker.fetchers.osm import OSMGraphFetcher
from graphfaker.fetchers.flights import FlightGraphFetcher
from graphfaker.logger import logger
from graphfaker.utils import parse_date_range
import os

app = typer.Typer()

Expand Down Expand Up @@ -52,6 +54,32 @@ def gen(
help="Year, Month and day range (YYYY-MM-DD,YYYY-MM-DD) for flight data. e.g. '2024-01-01,2024-01-15'.",
),

# for FetcherType.TRUST source
total_users: int = typer.Option(
10000, help="Number of user nodes for trust graph."
),
avg_trust_links: int = typer.Option(
15, help="Average outgoing trust edges per user."
),
reciprocity: float = typer.Option(
0.7, help="Fraction of trust edges that are mutual (0.0 to 1.0)."
),
num_communities: int = typer.Option(
None, help="Number of community clusters. Defaults to sqrt(total_users)."
),
community_mixing: float = typer.Option(
0.15, help="Fraction of edges crossing community boundaries (0.0 to 1.0)."
),
avg_distrust_links: float = typer.Option(
2.0, help="Average DISTRUSTS edges per user (Poisson-distributed)."
),
bot_fraction: float = typer.Option(
0.10, help="Fraction of nodes that are bots (0.0 to 1.0)."
),
seed: int = typer.Option(
None, help="Random seed for reproducibility."
),

# common
export: str = typer.Option("graph.graphml", help="File path to export GraphML"),
):
Expand Down Expand Up @@ -83,7 +111,23 @@ def gen(
logger.info(
f"Fetched OSM graph with {g.number_of_nodes()} nodes and {g.number_of_edges()} edges."
)
else:
elif fetcher == FetcherType.TRUST:
g = gf.generate_graph(
source="trust",
total_users=total_users,
avg_trust_links=avg_trust_links,
reciprocity=reciprocity,
num_communities=num_communities,
community_mixing=community_mixing,
avg_distrust_links=avg_distrust_links,
bot_fraction=bot_fraction,
seed=seed,
)
logger.info(
f"Generated trust graph with {g.number_of_nodes()} nodes and {g.number_of_edges()} edges."
)

elif fetcher == FetcherType.FLIGHTS:
# Flight fetcher
parsed_date_range = parse_date_range(date_range) if date_range else None

Expand Down
55 changes: 54 additions & 1 deletion graphfaker/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from faker import Faker
from graphfaker.fetchers.osm import OSMGraphFetcher
from graphfaker.fetchers.flights import FlightGraphFetcher
from graphfaker.fetchers.trust import TrustGraphFetcher
from graphfaker.logger import logger

fake = Faker()
Expand Down Expand Up @@ -289,6 +290,35 @@ def _generate_faker(self, total_nodes=100, total_edges=1000):
self.generate_edges(total_edges=total_edges)
return self.G

def _generate_trust(
self,
total_users: int = 10000,
avg_trust_links: int = 15,
reciprocity: float = 0.7,
num_communities: Optional[int] = None,
community_mixing: float = 0.15,
avg_distrust_links: float = 2.0,
bot_fraction: float = 0.10,
seed: Optional[int] = None,
):
"""Generate a directed social trust graph via TrustGraphFetcher."""
try:
G = TrustGraphFetcher.build_graph(
total_users=total_users,
avg_trust_links=avg_trust_links,
reciprocity=reciprocity,
num_communities=num_communities,
community_mixing=community_mixing,
avg_distrust_links=avg_distrust_links,
bot_fraction=bot_fraction,
seed=seed,
)
self.G = G
return G
except Exception as e:
logger.error(f"Failed to generate trust graph: {e}")
raise

def generate_graph(
self,
source: str = "faker",
Expand All @@ -305,6 +335,15 @@ def generate_graph(
year: int = 2024,
month: int = 1,
date_range: Optional[tuple] = None,
# Trust graph parameters
total_users: int = 10000,
avg_trust_links: int = 15,
reciprocity: float = 0.7,
num_communities: Optional[int] = None,
community_mixing: float = 0.15,
avg_distrust_links: float = 2.0,
bot_fraction: float = 0.10,
seed: Optional[int] = None,
) -> nx.DiGraph:
"""
Unified entrypoint: choose 'random' or 'osm'.
Expand Down Expand Up @@ -338,8 +377,22 @@ def generate_graph(
month=month,
date_range=date_range,
)
elif source == "trust":
return self._generate_trust(
total_users=total_users,
avg_trust_links=avg_trust_links,
reciprocity=reciprocity,
num_communities=num_communities,
community_mixing=community_mixing,
avg_distrust_links=avg_distrust_links,
bot_fraction=bot_fraction,
seed=seed,
)
else:
raise ValueError(f"Unknown source '{source}'. Use 'random' or 'osm'.")
raise ValueError(
f"Unknown source '{source}'. "
f"Use 'faker', 'osm', 'flights', or 'trust'."
)

def export_graph(self, G: nx.Graph = None, source: str = None, path: str = "graph.graphml"):
"""
Expand Down
1 change: 1 addition & 0 deletions graphfaker/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class FetcherType(str, Enum):
OSM = "osm"
FLIGHTS = "flights"
FAKER = "faker"
TRUST = "trust"
Loading