Skip to content
Merged
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
103 changes: 103 additions & 0 deletions audit_log_parsing/exercise_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from collections import defaultdict, Counter

LINES = [
"2026-08-01 10:15,alice,LOGIN,ok",
"2026-08-01 10:16,bob,LOGIN,ok",
"2026-08-01 10:17,alice,UPLOAD,ok",
"2026-08-01 10:20,bob,DELETE,fail",
"garbage line",
"2026-08-02 09:00,alice,LOGIN,fail",
"2026-08-02 09:01,alice,LOGIN,ok",
"2026-08-02 09:05,carol,UPLOAD,ok",
"2026-08-02 09:06,bob,UPLOAD,ok",
"2026-08-02 11:30,carol,DELETE,ok",
"2026-08-01 23:59,bob,LOGIN,ok,extra",
]
class InvalidParsingError(Exception): pass
def parse_line(line):
# try:
# parts = line.split(",")
# except AttributeError:
# raise InvalidParsingError("Invalid parsing")
# if len(parts) != 4:
# raise InvalidParsingError
# timestamp, user, action, status = parts
# if status.strip() not in ("ok", "fail"):
# raise InvalidParsingError
#
try:
timestamp, user, action, status = line.split(",")
except (AttributeError, ValueError):
raise InvalidParsingError("Invalid parsing")
if status.strip() not in ("ok", "fail"):
raise InvalidParsingError
else:
return {"ts": timestamp.strip(), "user": user.strip(), "action": action.strip(), "status": status.strip()}

def parse_all(lines):
valid_lines = []
for line in lines:
try:
valid_line = parse_line(line)
valid_lines.append(valid_line)
except InvalidParsingError:
continue
return valid_lines

def actions_per_user(records):
user_list = defaultdict(list)
for record in records:
user_list[record["user"]].append(record["action"])
result = {}
for user, action in user_list.items():
result[user] = len(action)
return result

def user_with_most_failures(records):
fails = Counter(r["user"] for r in records if r["status"] == "fail")
if not fails:
return None
user = min(fails, key=lambda u:(-fails[u], u))
return user, fails[user]

def counts_by_day(records):
return dict(Counter(r["ts"].split()[0] for r in records))

def busiest_day(records):
# day_list = defaultdict(list)
# for r in records:
# date = r["ts"].split()[0]
# day_list[date].
return Counter(r["ts"].split()[0] for r in records).most_common(1)[0][0]

def success_rate(records):
success_count = 0
if not records:
return 0.0
for r in records:
if r["status"] == "ok":
success_count+=1
return round(success_count / len(records), 2)

def main():
# recs = parse_all(LINES)
# print(f"action per user: {action_per_user(recs)}")
# print(f"user with most failures: {user_with_most_failures(recs)}")
# print(f"counts by date: {counts_by_day(recs)}")
# print(f"Busiest day: {busiest_day(recs)}")
# print(success_rate(recs))
#print(recs)

recs = parse_all(LINES)
print("records :", len(recs), " want 9")
print("per user :", actions_per_user(recs))
print("most fail :", user_with_most_failures(recs), " want ('alice', 1)")
print("by day :", counts_by_day(recs))
print("busiest :", busiest_day(recs), " want 2026-08-02")
print("success :", success_rate(recs), " want 0.78")
print("empty rate:", success_rate([]), " want 0.0")
print("no-fail :", user_with_most_failures(
[{"ts": "x", "user": "z", "action": "A", "status": "ok"}]), " want None")

if __name__ == "__main__":
main()
83 changes: 83 additions & 0 deletions audit_log_parsing/exercise_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from collections import defaultdict, Counter

LINES = [
"2026-08-01 10:15,alice,LOGIN,ok",
"2026-08-01 10:16,bob,LOGIN,ok",
"2026-08-01 10:17,alice,UPLOAD,ok",
"2026-08-01 10:20,bob,DELETE,fail",
"garbage line",
"2026-08-02 09:00,alice,LOGIN,fail",
"2026-08-02 09:01,alice,LOGIN,ok",
"2026-08-02 09:05,carol,UPLOAD,ok",
"2026-08-02 09:06,bob,UPLOAD,ok",
"2026-08-02 11:30,carol,DELETE,ok",
"2026-08-01 23:59,bob,LOGIN,ok,extra",
]
class InvalidParseError(Exception): pass

def parse_line(line):
try:
ts, user, action, status = line.split(",")
except (AttributeError, ValueError):
raise InvalidParseError("Invalid parse")

if status.strip() not in ("ok", "fail"):
raise InvalidParseError
else:
return {"ts":ts.strip(), "user": user.strip(), "action": action.strip(), "status":status.strip()}

def parse_all(lines):
result = []
for line in lines:
try:
result.append(parse_line(line))
except InvalidParseError:
continue
return result

def actions_per_user(records):
user_actions = defaultdict(list)
for r in records:
user_actions[r["user"]].append(r["action"])
result = {}
for user, action in user_actions.items():
result[user] = len(action)
return result

def user_with_most_failures(records):
failures = Counter(r["user"] for r in records if r["status"] == "fail")
if not failures:
return None
user = min(failures, key=lambda u: (-failures[u], u))
return user, failures[user]

def counts_by_day(records):
return Counter(r["ts"].split()[0] for r in records)

def busiest_day(records):
return Counter(r["ts"].split()[0] for r in records).most_common(1)[0][0]

def success_rate(records):
success_count = 0
if not records:
return 0.0
for r in records:
if r["status"] == "ok":
success_count+=1
return round(success_count / len(records), 2)

def main():
recs = parse_all(LINES)
print(recs)
print("records :", len(recs), " want 9")
print("per user :", actions_per_user(recs))
print("most fail :", user_with_most_failures(recs), " want ('alice', 1)")
print("by day :", counts_by_day(recs))
print("busiest :", busiest_day(recs), " want 2026-08-02")
print("success :", success_rate(recs), " want 0.78")
print("empty rate:", success_rate([]), " want 0.0")
print("no-fail :", user_with_most_failures(
[{"ts": "x", "user": "z", "action": "A", "status": "ok"}]), " want None")

if __name__ == "__main__":
main()
43 changes: 43 additions & 0 deletions audit_log_parsing/task
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Audit Log Parsing
Same family as your log/sensor/word-pair drills — parse lines, skip bad ones, aggregate. Blank file.

Each line: timestamp,user,action,status

timestamp is "YYYY-MM-DD HH:MM"
status is "ok" or "fail"
LINES = [
"2026-08-01 10:15,alice,LOGIN,ok",
"2026-08-01 10:16,bob,LOGIN,ok",
"2026-08-01 10:17,alice,UPLOAD,ok",
"2026-08-01 10:20,bob,DELETE,fail",
"garbage line",
"2026-08-02 09:00,alice,LOGIN,fail",
"2026-08-02 09:01,alice,LOGIN,ok",
"2026-08-02 09:05,carol,UPLOAD,ok",
"2026-08-02 09:06,bob,UPLOAD,ok",
"2026-08-02 11:30,carol,DELETE,ok",
"2026-08-01 23:59,bob,LOGIN,ok,extra",
]
Functions
parse_line(line) → dict {"ts", "user", "action", "status"}, or None if invalid.
Invalid = not exactly 4 fields (so "garbage line" and the ,extra line are out), or status is neither "ok" nor "fail". Strip whitespace from each field.

parse_all(lines) → list of dicts, malformed lines dropped.

actions_per_user(records) → dict {user: total_action_count}. Total actions, not distinct.

user_with_most_failures(records) → (user, count), or None if there are no failures. Ties break alphabetically.

counts_by_day(records) → dict {"YYYY-MM-DD": count}. The day is the date part of the timestamp.

busiest_day(records) → the "YYYY-MM-DD" with the most records. Tie breaks earliest (alphabetical works here).

success_rate(records) → float, fraction that are "ok", rounded to 2 decimals. 0.0 if there are no records — don't divide by zero.

Expected
valid records : 9
actions_per_user : {'alice': 4, 'bob': 3, 'carol': 2}
user_with_most_failures : one of ('alice',1)/('bob',1) — alice wins the tie
counts_by_day : {'2026-08-01': 4, '2026-08-02': 5}
busiest_day : '2026-08-02'
success_rate : 0.78
22 changes: 22 additions & 0 deletions dsa/d1/exercise_5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
def main():
nums = [2, 7, 11, 15, 3, 4, 8, 1]
target = 15

print(two_sum(nums, target))

def two_sum(nums, target):
counts = {}
pairs = set()
for i in range(len(nums)):
required_num = target - nums[i]
if required_num in counts:
#return [ counts[required_num], i]
if required_num>nums[i]:
pairs.add((nums[i], required_num))
else:
pairs.add((required_num, nums[i]))
counts[nums[i]] = i
return sorted(pairs)

if __name__ == "__main__":
main()
24 changes: 24 additions & 0 deletions dsa/d2/exercise_5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def main():
s = "xyzxy"
print(longest_substring(s))

def longest_substring(s):

left = 0
seen = set()
best_start = 0
max_length = 0
for right in range(len(s)):

while s[right] in seen:
seen.remove(s[left])
left+=1
seen.add(s[right])

if right-left+1 > max_length:
max_length = right-left+1
best_start=left
return s[best_start:best_start+max_length]

if __name__ == "__main__":
main()
26 changes: 26 additions & 0 deletions dsa/d4/exercise_6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
def main():
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] # → [4, -1, 2, 1]
print(kadane_algorithm(nums))

def kadane_algorithm(nums):
current_sum = nums[0]
max_sum = nums[0]
best_start = 0
best_end = 0
temp_start = 0

for i in range(1, len(nums)):
if current_sum + nums[i] > nums[i]:
current_sum = current_sum + nums[i]
else:
current_sum = nums[i]
temp_start = i

if current_sum > max_sum:
max_sum = current_sum
best_start = temp_start
best_end = i
return nums[best_start:best_end+1]

if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions dsa/d5/exercise_5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from collections import defaultdict


def main():
words = ["eat", "tea", "tan", "ate", "nat", "bat"]
print(anagram_groups(words))

def anagram_groups(words):
result = defaultdict(list)

for word in words:
key = "".join(sorted(word))
result[key].append(word)
return dict(result)

if __name__ == "__main__":
main()
34 changes: 34 additions & 0 deletions dsa/e1/exercise_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def main():
string = 'eceba'
k = 2
print(longest_k_distinct(string,k))

def longest_k_distinct(s,k):
left = 0
seen = {}
best_start = 0
max_length=0
for right in range(len(s)):
# while s[right] in seen:
#
# seen.remove(s[left])
# left+=1
# seen.append(s[right])
# #print(right-left)
seen[s[right]] = seen.get(s[right], 0) + 1
while len(seen)>k:
seen[s[left]] -= 1

if seen[s[left]] == 0:
del seen[s[left]]
left+=1

if right-left+1 > max_length:
max_length = right-left+1
best_start=left


return s[best_start:best_start+max_length]

if __name__ == "__main__":
main()
28 changes: 28 additions & 0 deletions dsa/e1/exercise_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
def main():
string = 'eceba'
k = 2
print(longest_k_distinct(string,k))

def longest_k_distinct(s,k):
count = {}
left = 0
max_length = 0
best_start = 0

for right in range(len(s)):
count[s[right]] = count.get(s[right], 0) + 1

while len(count) > k:
count[s[left]] -=1
if count[s[left]] == 0:
del count[s[left]]
left+=1

if right-left+1 > max_length:
max_length = right-left+1
best_start=left
return s[best_start:best_start+max_length]

if __name__ == "__main__":
main()

Loading
Loading