From 729650db978d1f3abe24691625b8582274fbc612 Mon Sep 17 00:00:00 2001 From: Erik Lacson Date: Tue, 15 Jul 2025 16:17:26 +0800 Subject: [PATCH 1/2] added line in actions workflow yaml to make the run button appear --- .github/workflows/python-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml index db3dcd1..35d3ede 100644 --- a/.github/workflows/python-lint.yml +++ b/.github/workflows/python-lint.yml @@ -4,6 +4,7 @@ on: push: branches: [main] pull_request: + workflow_dispatch: jobs: lint: From d8e9957db03894ce570b13d196ba53ca0bc0a02b Mon Sep 17 00:00:00 2001 From: Erik Lacson Date: Wed, 23 Jul 2025 10:48:58 +0800 Subject: [PATCH 2/2] added Black module --- .gitignore | 10 ++--- FetchEvent.py | 42 +++++++++++------- __pycache__/FetchEvent.cpython-310.pyc | Bin 4048 -> 0 bytes ...st_FetchEvent.cpython-310-pytest-8.4.1.pyc | Bin 5088 -> 0 bytes tests/test_FetchEvent.py | 22 ++++++--- 5 files changed, 47 insertions(+), 27 deletions(-) delete mode 100644 __pycache__/FetchEvent.cpython-310.pyc delete mode 100644 tests/__pycache__/test_FetchEvent.cpython-310-pytest-8.4.1.pyc diff --git a/.gitignore b/.gitignore index 987f286..f903a64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - # Unit test / coverage reports htmlcov/ .tox/ @@ -8,7 +7,7 @@ htmlcov/ .cache coverage.xml *.coveragerc -*.py,cover +*.py.cover .hypothesis/ # Pytest @@ -20,8 +19,6 @@ coverage.xml env/ venv/ ENV/ -env.bak/ -venv.bak/ # Python version specific .python-version @@ -49,4 +46,7 @@ venv.bak/ .dmy #Output files -*.csv \ No newline at end of file +*.csv + +#Cache +__pycache__/ \ No newline at end of file diff --git a/FetchEvent.py b/FetchEvent.py index 942fc5b..ff8c0fd 100644 --- a/FetchEvent.py +++ b/FetchEvent.py @@ -11,15 +11,18 @@ from datadog_api_client import ApiClient, Configuration from datadog_api_client.v2.api.events_api import EventsApi + def initialize_config(): """ Sets up configurations, API keys, and Datadog API client. """ if not load_dotenv(): - raise EnvironmentError("Unable to load .env file. Make sure it exists and contains the required keys.") + raise EnvironmentError( + "Unable to load .env file. Make sure it exists and contains the required keys." + ) - DD_API_KEY = os.getenv('DD_API_KEY') - DD_APP_KEY = os.getenv('DD_APP_KEY') + DD_API_KEY = os.getenv("DD_API_KEY") + DD_APP_KEY = os.getenv("DD_APP_KEY") if not DD_API_KEY or not DD_APP_KEY: raise ValueError("Datadog API Key or App Key is missing in the .env file.") @@ -30,6 +33,7 @@ def initialize_config(): return configuration + def parse_arguments(): """ Parses command-line arguments. @@ -39,16 +43,17 @@ def parse_arguments(): "--query", type=str, required=True, - help="The filter query for fetching events, e.g., 'service:trms production service check'." + help="The filter query for fetching events, e.g., 'service:trms production service check'.", ) parser.add_argument( "--days", type=int, default=30, - help="Number of days in the past to fetch events (default: 30)." + help="Number of days in the past to fetch events (default: 30).", ) return parser.parse_args() + def fetch_event_data(events_api, query, days): """ Fetches event data from Datadog API. @@ -60,14 +65,15 @@ def fetch_event_data(events_api, query, days): events_response = events_api.list_events( filter_from=str(start_time), filter_to=str(end_time), - filter_query=query, - page_limit=1000 + filter_query=query, + page_limit=1000, ) return events_response.data if events_response.data else [] except Exception as e: handle_errors(f"Error fetching events: {e}") return [] + def process_event_data(events_data): """ Processes and transforms the fetched event data. @@ -85,14 +91,16 @@ def process_event_data(events_data): event_timestamp_local = event_timestamp.astimezone(target_timezone) - processed_events.append({ - "event_id": event.id, - "event_type": event.type, - "event_tags": ", ".join(event.attributes.tags) if event.attributes.tags else "", - "event_timestamp_local": event_timestamp_local.strftime("%Y-%m-%d %H:%M:%S"), - "event_title": attributes.get("title", "No Title"), - "event_status": attributes.get("status", "Unknown") - }) + processed_events.append( + { + "event_id": event.id, + "event_type": event.type, + "event_tags": ", ".join(event.attributes.tags) if event.attributes.tags else "", + "event_timestamp_local": event_timestamp_local.strftime("%Y-%m-%d %H:%M:%S"), + "event_title": attributes.get("title", "No Title"), + "event_status": attributes.get("status", "Unknown"), + } + ) return processed_events @@ -114,12 +122,14 @@ def save_to_csv(events_df, output_dir_path="output", output_file="output/events_ except Exception as e: handle_errors(f"Error saving to CSV: {e}") + def handle_errors(error_message): """ Centralized error handling mechanism. """ print(error_message) + def main(): """ Orchestrates the workflow and executes the main logic. @@ -138,6 +148,6 @@ def main(): else: print("No events found.") + if __name__ == "__main__": main() - diff --git a/__pycache__/FetchEvent.cpython-310.pyc b/__pycache__/FetchEvent.cpython-310.pyc deleted file mode 100644 index 04775376d6777dd88212b288d34fae3a94f6a626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4048 zcmai1&2QYs73Yv#E_b#1ux#0GkPkMfl5A?N^3fu#nx=MTr!5RSPUXb)0x7|8MiM11 zxtW=jyzD9ns-Wm4=q;BtfOPDwhhF+m^beS8FU?<2)CijX-f$($&Y>hY^Je(?IDGHN z-+NQe&bA#Kzx_wP{r3gO`6ms|9v%i)ajCzd;SOiH)31BhXBfM=JMj7*tLOZF!|DY6 z!1m34)AnIMMDOLTL95@gx_;gs%=BjloqorT8~N;Du0IDk;LT5+{yYzP3;hCb^BMH# zc!$rTU*wng0zdaD>!0U~{5)nZ@C$qi{gSxIFYdbXH)83~fgQGCukPiquyW^imzkNo zOnA&oEs8x1L!M}%vw`Tkrgd$Yt>>92w3%Hmi_L6nBom#L1@u}s_AsxYk&>m8gFHjm zIxckwjdl(l)zpl$L-u>7-6M~?$Lzqdk_U-R9Jw@M>O0!!zSU_QItT8=nYcRvZ@lSD zn3V!Z?uOHCR_pBu$A-`<8V#fL?84NQ=-QoMMZ01QDJgh#J<$m-x9n_cKc#1%u5P_o zBp>D?(q%*rh)sV&JMuB-Om7$iL>K!PkY?w4IX^v zvwEfpsNhH7?Rw^@aqIvXGu2Brw09DzghI3i14L~l&k7MGa%)7cQq$-^Yd5hVr0y~lxH+8c}ubDqps(t=lOsc2RbbOz= z(=+(m4<5;3AW17F>Nq(Fs6@jS8GnGF9nFr|v2zG~Oxz33W#_|gk{u5V^)@Wg~U)0z%+X*yHU zf{OgSCE`3AWV*U!|Kleywiew67Z5Oj9ndv+97Gk@0F=+zd0G>Q`avozjLqyeeu!Me zf(S!(o2Ff>$6yE3sNd`pwoF^;MC#ZY4n@IhX@06Mg&LLxV8g_dv0tz-td|kf`+tCy zC|c$Pzr$SDX8}$MeDL*!V2;hP1r~nd{#U*Rm6XWf0(aM|#G4q?kVVA?rC@R2o; zAFewe{Okb1-f?%_1K;*u9cb>39QhoA-2j;c005);qvnJ%PcUIeA+%e#+V-g#`9tm> zb!z#T6^u6~!O<)aj@iUNn&V9#9=lcoYC;t^J+L^N|vWy$KTq{#AwvT>lRtk^6o_ex}(>fG}EmF2<8GLM#j zxwd?3ZF$2qbf$Arg}2M-E=eXpsPssg=6l6%QSKM?U)$Hj8J`9?w&YR=JJ!jTGE21v z#TSnchH+k|Np5CNrpa)p9@OT$^Tr>Jb!9?&rYZ}8tfaNfJ{)PGOzYH=d>x@OO(}+X zk_zLEbP7#%`c!~i20$4%<25Pyc0vA5nH44k(QJ~c@sP;?(0W12BFQX(L9L7V6y|0g zD21X$r(ILSu328CHPmRe_v|;ypJ4V$ye>{{oweX)LB;i`m#V$TXwAnXr?mE045|f6d7vsvdM-m{#JveS zA)eD929{1N}pG7{|@7V#U&9?(wkJ`KznfKyINqV#E8=aTf>=ZABHA zyVb?(D4JVWt5Hg**c|2g zINFz)MhQeOH>_7J)uvRkM}{zo^{ho8PGS}l4p8aAx-yNSr1aK&XTv@qU$$}8WsK;_ zE(T!k)-EFmeIq}B-eZ(V^hnj8r9}Dyje3ZJNo9T4tyZm**HPz4Takfpi0LCs`{aOuNFirx zP_qvRN8W=O_;l84#-Ia*%=5(Rtvg0gRP^?aZ^tRb(}OBFswwTt{s@7}eiZo(EsFjL zivK^oazMEm*%etE0JiW1Js)}?Z(qKko0PE+8akXb=$mw_?u(8NY4czq>-vD~A#JR% z6##@E8^7wPpYB7C&il04q3wcKUA10$N21sQy#Qu4X7@|EyP22!mafGEkrI(vG!IbZ zV^gq|rBjk#0NK{TN3}O9c<*+%ZR-_V&Pw{DC3(OmF?ofg6>5kbtb=VRLCiiCPI3X$ zw!V++9f|xt>D{EJPYspfbz{pZpVsp`%&Di)1bzT23XwK_w3dq8xqrLO=l>%U+%{nx zlYSg`UHL8<`Zb#Fr5f=5jw%Zi)SJC}BY_&O;E6KskV~>JU!dkKYCJS%k&-xeP*fwF ztk-_9-B*0?+jo0=ul1nYoBk~jYu`}iUptLbeeD}UxCPVc+KXf$;`odUtTU`1!Z!yf npmXsS{RZkKG_=X=#8rzt5x4jv9)t^KOH>V4mP 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 diff --git a/tests/test_FetchEvent.py b/tests/test_FetchEvent.py index cc483f5..6a985ab 100644 --- a/tests/test_FetchEvent.py +++ b/tests/test_FetchEvent.py @@ -9,29 +9,37 @@ fetch_event_data, process_event_data, save_to_csv, - handle_errors + handle_errors, ) + def test_initialize_config(): - with patch("FetchEvent.load_dotenv", return_value=True), \ - patch("FetchEvent.os.getenv", side_effect=["dummy_api_key", "dummy_app_key"]): + with patch("FetchEvent.load_dotenv", return_value=True), patch( + "FetchEvent.os.getenv", side_effect=["dummy_api_key", "dummy_app_key"] + ): config = initialize_config() assert config.api_key["apiKeyAuth"] == "dummy_api_key" assert config.api_key["appKeyAuth"] == "dummy_app_key" + def test_parse_arguments(): test_args = ["--query", "test_query", "--days", "7"] - with patch("FetchEvent.argparse.ArgumentParser.parse_args", return_value=argparse.Namespace(query="test_query", days=7)): + with patch( + "FetchEvent.argparse.ArgumentParser.parse_args", + return_value=argparse.Namespace(query="test_query", days=7), + ): args = parse_arguments() assert args.query == "test_query" assert args.days == 7 + def test_fetch_event_data(): mock_events_api = MagicMock() mock_events_api.list_events.return_value = MagicMock(data=["event1", "event2"]) events = fetch_event_data(mock_events_api, "test_query", 7) assert events == ["event1", "event2"] + def test_process_event_data(): mock_event_data = [ MagicMock( @@ -40,8 +48,8 @@ def test_process_event_data(): attributes=MagicMock( tags=["tag1", "tag2"], timestamp=pd.Timestamp("2023-01-01 00:00:00"), - attributes={"title": "Event Title", "status": "Active"} - ) + attributes={"title": "Event Title", "status": "Active"}, + ), ) ] result = process_event_data(mock_event_data) @@ -49,6 +57,7 @@ def test_process_event_data(): assert len(result) > 0 assert result[0]["event_title"] == "Event Title" + def test_save_to_csv(tmp_path): test_df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]}) output_dir = tmp_path / "output" @@ -56,6 +65,7 @@ def test_save_to_csv(tmp_path): save_to_csv(test_df, str(output_dir), str(output_file)) assert os.path.exists(output_file) + def test_handle_errors(capfd): handle_errors("Test error message") captured = capfd.readouterr()