Simon commited on
Commit
0dbcacb
·
1 Parent(s): 09d041b

Add unit tests for sitemap parser and URL extraction functions (#8)

Browse files
examples/upload_to_hopsworks.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ import hopsworks
5
+ import pandas as pd
6
+
7
+ project_root = Path(__file__).parent.parent
8
+ sys.path.insert(0, str(project_root / "src"))
9
+
10
+ from phising_detection.utils.hopsworks_utils import (
11
+ connect_to_hopsworks,
12
+ get_or_create_feature_group,
13
+ upload_dataframe_to_feature_group
14
+ )
15
+
16
+
17
+ def upload_legit_urlcsv_to_hopsworks():
18
+
19
+
20
+ # Load the legitimate URLs dataset
21
+ legit_urls_df = pd.read_csv("../src/phising_detection/data/data_files/legitimate-urls-extracted.csv",)
22
+
23
+ # Connect to Hopsworks project
24
+ project = connect_to_hopsworks()
25
+
26
+ # Define feature group parameters
27
+ feature_group_name = "scan_progress_legit_urls"
28
+ version = 1
29
+ description = "Feature group for legitimate URLs dataset"
30
+ primary_key = ["domain"]
31
+ event_time = None
32
+ online_enabled = False
33
+
34
+ # Upload the DataFrame to Hopsworks feature group
35
+ upload_dataframe_to_feature_group(
36
+ project=project,
37
+ df=legit_urls_df,
38
+ feature_group_name=feature_group_name,
39
+ version=version,
40
+ description=description,
41
+ primary_key=primary_key,
42
+ event_time=event_time,
43
+ online_enabled=online_enabled
44
+ )
45
+
46
+ if __name__ == "__main__":
47
+ upload_legit_urlcsv_to_hopsworks()
pyproject.toml CHANGED
@@ -4,9 +4,11 @@ version = "0.1.0"
4
  description = "Add your description here"
5
  requires-python = ">=3.12"
6
  dependencies = [
7
- "pandas>=2.3.3",
8
- "pandas-stubs==2.3.3.251219",
 
9
  "python-dotenv>=1.0.0",
 
10
  "requests>=2.32.5",
11
  ]
12
 
 
4
  description = "Add your description here"
5
  requires-python = ">=3.12"
6
  dependencies = [
7
+ "confluent-kafka>=2.3.0",
8
+ "hopsworks>=4.2.0",
9
+ "pandas>=2.1.0,<2.2.0",
10
  "python-dotenv>=1.0.0",
11
+ "pyarrow>=14.0.0",
12
  "requests>=2.32.5",
13
  ]
14
 
src/phising_detection/data/load_data.py CHANGED
@@ -22,7 +22,7 @@ def load_phishing_urls(
22
  """
23
  if file_path is None:
24
  # Default to the file in the same directory as this module
25
- file_path = Path(__file__).parent / "phishing-links-ACTIVE.txt"
26
  else:
27
  file_path = Path(file_path)
28
 
 
22
  """
23
  if file_path is None:
24
  # Default to the file in the same directory as this module
25
+ file_path = Path(__file__).parent / "data_files" /"phishing-links-ACTIVE.txt"
26
  else:
27
  file_path = Path(file_path)
28
 
src/phising_detection/utils/hopsworks_utils.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for connecting to and interacting with Hopsworks Feature Store."""
2
+
3
+ import os
4
+ import logging
5
+ from typing import Optional
6
+ import pandas as pd
7
+ import hopsworks
8
+ from dotenv import load_dotenv
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Load environment variables
13
+ load_dotenv()
14
+
15
+
16
+ def connect_to_hopsworks(api_key: Optional[str] = None, project_name: Optional[str] = None):
17
+ """
18
+ Connect to Hopsworks.
19
+
20
+ Args:
21
+ api_key: Hopsworks API key. If None, reads from HOPSWORKS_API_KEY env variable.
22
+ project_name: Hopsworks project name. If None, reads from HOPSWORKS_PROJECT_NAME env variable.
23
+
24
+ Returns:
25
+ Hopsworks project object
26
+
27
+ Raises:
28
+ ValueError: If API key or project name is not provided
29
+ """
30
+ # Get API key from parameter or environment
31
+ api_key = api_key or os.getenv("HOPSWORKS_API_KEY")
32
+ if not api_key:
33
+ raise ValueError(
34
+ "Hopsworks API key not provided. "
35
+ "Set HOPSWORKS_API_KEY environment variable or pass api_key parameter."
36
+ )
37
+
38
+ # Get project name from parameter or environment
39
+ project_name = project_name or os.getenv("HOPSWORKS_PROJECT")
40
+
41
+ logger.info(f"Connecting to Hopsworks project: {project_name or 'default'}")
42
+
43
+ # Login to Hopsworks
44
+ try:
45
+ if project_name:
46
+ project = hopsworks.login(
47
+ api_key_value=api_key,
48
+ project=project_name,
49
+ engine="python" # Use Python engine (serverless, no cert download)
50
+ )
51
+ else:
52
+ project = hopsworks.login(
53
+ api_key_value=api_key,
54
+ engine="python"
55
+ )
56
+ except Exception as e:
57
+ logger.error(f"Failed to connect to Hopsworks: {e}")
58
+ logger.info("Trying to connect without specifying project...")
59
+ project = hopsworks.login(
60
+ api_key_value=api_key,
61
+ engine="python"
62
+ )
63
+
64
+ logger.info(f"Successfully connected to Hopsworks project: {project.name}")
65
+ return project
66
+
67
+
68
+ def get_or_create_feature_group(
69
+ project,
70
+ name: str,
71
+ version: int = 1,
72
+ description: str = "",
73
+ primary_key: list = None,
74
+ event_time: Optional[str] = None,
75
+ online_enabled: bool = False
76
+ ):
77
+ """
78
+ Get existing feature group or create new one if it doesn't exist.
79
+
80
+ Args:
81
+ project: Hopsworks project object
82
+ name: Feature group name
83
+ version: Feature group version
84
+ description: Description of the feature group
85
+ primary_key: List of column names to use as primary key
86
+ event_time: Column name to use as event time
87
+ online_enabled: Whether to enable online feature serving
88
+
89
+ Returns:
90
+ Feature group object
91
+ """
92
+ fs = project.get_feature_store()
93
+
94
+ try:
95
+ # Try to get existing feature group
96
+ fg = fs.get_feature_group(name=name, version=version)
97
+ logger.info(f"Retrieved existing feature group: {name} (version {version})")
98
+ return fg
99
+ except Exception:
100
+ # Feature group doesn't exist, will need to create it
101
+ logger.info(f"Feature group {name} (version {version}) not found, will create on first insert")
102
+ return None
103
+
104
+
105
+ def upload_dataframe_to_feature_group(
106
+ project,
107
+ df: pd.DataFrame,
108
+ feature_group_name: str,
109
+ version: int = 1,
110
+ description: str = "",
111
+ primary_key: list = None,
112
+ event_time: Optional[str] = None,
113
+ online_enabled: bool = False,
114
+ write_options: dict = None
115
+ ):
116
+ """
117
+ Upload a DataFrame to a Hopsworks feature group.
118
+
119
+ Args:
120
+ project: Hopsworks project object
121
+ df: Pandas DataFrame to upload
122
+ feature_group_name: Name of the feature group
123
+ version: Feature group version
124
+ description: Description of the feature group
125
+ primary_key: List of column names to use as primary key
126
+ event_time: Column name to use as event time
127
+ online_enabled: Whether to enable online feature serving
128
+ write_options: Additional write options (e.g., {"wait_for_job": False})
129
+
130
+ Returns:
131
+ Feature group object
132
+ """
133
+ fs = project.get_feature_store()
134
+
135
+ logger.info(f"Uploading DataFrame to feature group: {feature_group_name} (version {version})")
136
+ logger.info(f"DataFrame shape: {df.shape}")
137
+
138
+ # Create or get feature group
139
+ fg = fs.get_or_create_feature_group(
140
+ name=feature_group_name,
141
+ version=version,
142
+ description=description,
143
+ primary_key=primary_key or [],
144
+ event_time=event_time,
145
+ online_enabled=online_enabled
146
+ )
147
+
148
+ # Insert data
149
+ write_options = write_options or {"wait_for_job": True}
150
+ fg.insert(df, write_options=write_options)
151
+
152
+ logger.info(f"Successfully uploaded {len(df)} rows to {feature_group_name}")
153
+ return fg
154
+
155
+
156
+ def read_feature_group(
157
+ project,
158
+ feature_group_name: str,
159
+ version: int = 1,
160
+ online: bool = False
161
+ ) -> pd.DataFrame:
162
+ """
163
+ Read data from a Hopsworks feature group.
164
+
165
+ Args:
166
+ project: Hopsworks project object
167
+ feature_group_name: Name of the feature group
168
+ version: Feature group version
169
+ online: Whether to read from online feature store
170
+
171
+ Returns:
172
+ Pandas DataFrame with feature group data
173
+ """
174
+ fs = project.get_feature_store()
175
+
176
+ logger.info(f"Reading feature group: {feature_group_name} (version {version})")
177
+
178
+ fg = fs.get_feature_group(name=feature_group_name, version=version)
179
+
180
+ if online:
181
+ df = fg.read(online=True)
182
+ else:
183
+ df = fg.read()
184
+
185
+ logger.info(f"Read {len(df)} rows from {feature_group_name}")
186
+ return df
187
+
188
+
189
+ def create_feature_view(
190
+ project,
191
+ name: str,
192
+ version: int = 1,
193
+ description: str = "",
194
+ query=None,
195
+ labels: list = None
196
+ ):
197
+ """
198
+ Create a feature view for training datasets.
199
+
200
+ Args:
201
+ project: Hopsworks project object
202
+ name: Feature view name
203
+ version: Feature view version
204
+ description: Description of the feature view
205
+ query: Query object to define feature selection
206
+ labels: List of label column names
207
+
208
+ Returns:
209
+ Feature view object
210
+ """
211
+ fs = project.get_feature_store()
212
+
213
+ logger.info(f"Creating feature view: {name} (version {version})")
214
+
215
+ fv = fs.create_feature_view(
216
+ name=name,
217
+ version=version,
218
+ description=description,
219
+ query=query,
220
+ labels=labels or []
221
+ )
222
+
223
+ logger.info(f"Successfully created feature view: {name}")
224
+ return fv
uv.lock CHANGED
The diff for this file is too large to render. See raw diff