Projects

Wordle Solver

Description

A solver for the popular game Wordle.

Jira to Slides

Description

A python script to generate a presentation from a Jira board.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 import collections import collections.abc from pptx import Presentation from pptx.util import Pt import configparser import requests as r config = configparser.ConfigParser() config.read('config.ini') try: RAPID_VIEW_ID = str(config['CONFIG']['RAPID_VIEW_ID']) JSESSIONID = str(config['CONFIG']['JSESSION_ID']) TEAM_NAME = str(config['CONFIG']['TEAM_NAME']) BACKLOG = config['CONFIG'].getboolean('BACKLOG') if(RAPID_VIEW_ID == '' or JSESSIONID == '' or TEAM_NAME == ''): raise ValueError() except ValueError: print('Invalid config.ini file. Please check your config.ini file and try again.') exit(1) JIRA_URL = f'https://issues.corp.rapid7.com \ /rest/greenhopper/1.0/xboard/plan/backlog/ \ data.json?rapidViewId={RAPID_VIEW_ID}&selectedProjectKey=LOG' COOKIES = {'JSESSIONID': JSESSIONID} print(f'Generating presentation for {TEAM_NAME}') print('Getting JIRA Data...') def getSprintData() -> dict: res = r.get(JIRA_URL, cookies=COOKIES) if res.status_code == 401: raise ConnectionError('Could not get JIRA Data. Check your JSESSIONID, it may have expired.') return res.json() SPRINT = getSprintData() EPIC_DATA = SPRINT['entityData']['epics'] ALL_ISSUES = SPRINT['issues'] THIS_SPRINT_ISSUE_IDS = SPRINT['sprints'][0]['issuesIds'] TITLE = f'{TEAM_NAME} Sprint Work' BUGS = 'Bugs' PRS = Presentation('template.pptx') LAYOUT = PRS.slide_layouts[11] class Issue: def __init__(self, summary, status, epic=None): self.summary = summary self.status = status self.epic = epic def __str__(self): return f"{self.summary} - {self.status}" class Epic: def addIssue(self, issue): self.issues.append(issue) def __init__(self, name): self.name = name self.issues = [] def __str__(self): return self.name def __len__(self): return len(self.issues) def __lt__(self, other): return len(self.issues) < len(other.issues) def getEpicName(epicId:str) -> str: return EPIC_DATA[epicId]['epicField']['text'] def getIssueStatus(statusId:str) -> str: return SPRINT['entityData']['statuses'][statusId]['statusName'] def isInThisSprint(issue:object) -> bool: return issue['id'] in THIS_SPRINT_ISSUE_IDS def isBug(issue:object) -> bool: return 'epicId' not in issue def getTotalFinishedBugs(bugs: list) -> str: return len(list(filter(lambda bug : bug.status == 'Done', bugs))) def getBugTitle(bugs: list) -> str: closedBugs = getTotalFinishedBugs(bugs) return f"Bugs ({closedBugs} closed, {len(bugs) - closedBugs} open ) " def createSlide(title:str): slide = PRS.slides.add_slide(LAYOUT) slide.shapes.title.text = title body = slide.placeholders[1] return body.text_frame def addEpicToSlide(slide, epic:Epic): paragraph = slide.paragraphs[0] if slide.paragraphs[0].text == '' else slide.add_paragraph() paragraph.text = epic.name paragraph.font.size = Pt(17.5) paragraph.font.bold = True for issue in epic.issues: if(BACKLOG and issue.status == 'Backlog'): continue paragraph = slide.add_paragraph() paragraph.text = str(issue) paragraph.font.size = Pt(13.5) paragraph.level = 1 #add a blank line paragraph = slide.add_paragraph() paragraph.text = '' epics = {} bugs = [] print('Parsing JIRA Data...') for issue in filter(isInThisSprint, ALL_ISSUES): if isBug(issue): bugs.append(Issue(issue['summary'], getIssueStatus(issue['statusId']))) continue issue = Issue(issue['summary'], getIssueStatus(issue['statusId']), getEpicName(issue['epicId'])) if issue.epic not in epics: epics[issue.epic] = Epic(issue.epic) epics[issue.epic].addIssue(issue) epicsSortedBySize = sorted(epics.values(), reverse=True) print(f'Generating Presentation for {len(epicsSortedBySize)} Epics, {len(bugs)} Bugs.') totalIssues = 0 totalEpics = 0 slide = createSlide(TITLE) for epic in epicsSortedBySize: if totalIssues + len(epic) > 10 or (totalEpics > 2 and totalIssues + len(epic) > 5): slide = createSlide(TITLE) totalIssues = 0 totalEpics = 0 addEpicToSlide(slide, epic) totalIssues += len(epic) totalEpics += 1 slide = createSlide(BUGS) slide.text = getBugTitle(bugs) slide.paragraphs[0].font.size = Pt(17.5) slide.paragraphs[0].font.bold = True for bug in bugs: paragraph = slide.add_paragraph() paragraph.text = str(bug) paragraph.font.size = Pt(13.5) paragraph.level = 1 #Remove Template Slide xml_slides = PRS.slides._sldIdLst slides = list(xml_slides) xml_slides.remove(slides[0]) PRS.save('Sprint.pptx') print('Success! \nSaved to Sprint.pptx')

Licence Plate Detector

Description

A licence plate detector and reader using YOLOv8 and EasyOCR.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 from ultralytics import YOLO import cv2 import easyocr from difflib import SequenceMatcher DETECT_EVERY_N_FRAMES = 1 license_plate_detector = YOLO("best.pt") reader = easyocr.Reader(['en']) accepted_license_plates = ['08-WX-TEST', '08-WX-TEST2'] def crop_license_plate(image, box): return image[box[1]:box[3], box[0]:box[2]] def process_license_plate(image): grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(grey, 80, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] return thresh def similar(str1, str2): return SequenceMatcher(None, str1, str2).ratio() video_stream = cv2.VideoCapture(0) frame_count = 0 while True: cropped_license_plate = None license_plate_text = '' ret, frame = video_stream.read() frame_count += 1 if ret and frame_count % DETECT_EVERY_N_FRAMES == 0: license_plate_results = license_plate_detector(frame, verbose=False)[0] for box in license_plate_results.boxes.xyxy.tolist(): box = [int(x) for x in box] cropped_license_plate = crop_license_plate(frame, box) processed_license_plate = cv2.cvtColor(cropped_license_plate, cv2.COLOR_BGR2GRAY) if processed_license_plate is not None: license_plate_text = reader.readtext(processed_license_plate, detail=0) cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2) if len(license_plate_text) > 0: cv2.putText(frame, license_plate_text[-1], (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 0, 0), 5, cv2.LINE_AA) if any([similar(license_plate_text[-1], accepted_license_plate) > 0.8 for accepted_license_plate in accepted_license_plates]): print(f'License plate detected! - {license_plate_text[-1]}') cv2.imwrite('license_plate.png', cropped_license_plate) cv2.putText(frame, 'Detected!', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 5, (255, 255, 0), 5, cv2.LINE_AA) cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break video_stream.release() cv2.destroyAllWindows()