Initial Commit
This commit is contained in:
commit
7ab024efda
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
8
job-extractor/.dockerignore
Normal file
8
job-extractor/.dockerignore
Normal file
@ -0,0 +1,8 @@
|
||||
# configurations
|
||||
.idea
|
||||
|
||||
# crawlee storage folder
|
||||
storage
|
||||
|
||||
# installed files
|
||||
node_modules
|
||||
7
job-extractor/.gitignore
vendored
Normal file
7
job-extractor/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# This file tells Git which files shouldn't be added to source control
|
||||
|
||||
.idea
|
||||
dist
|
||||
node_modules
|
||||
storage
|
||||
|
||||
52
job-extractor/Dockerfile
Normal file
52
job-extractor/Dockerfile
Normal file
@ -0,0 +1,52 @@
|
||||
# Specify the base Docker image. You can read more about
|
||||
# the available images at https://crawlee.dev/docs/guides/docker-images
|
||||
# You can also use any other image from Docker Hub.
|
||||
FROM apify/actor-node-playwright-chrome:20-1.50.1 AS builder
|
||||
|
||||
# Copy just package.json and package-lock.json
|
||||
# to speed up the build using Docker layer cache.
|
||||
COPY --chown=myuser package*.json ./
|
||||
|
||||
# Install all dependencies. Don't audit to speed up the installation.
|
||||
RUN npm install --include=dev --audit=false
|
||||
|
||||
# Next, copy the source files using the user set
|
||||
# in the base image.
|
||||
COPY --chown=myuser . ./
|
||||
|
||||
# Install all dependencies and build the project.
|
||||
# Don't audit to speed up the installation.
|
||||
RUN npm run build
|
||||
|
||||
# Create final image
|
||||
FROM apify/actor-node-playwright-chrome:20-1.50.1
|
||||
|
||||
# Copy only built JS files from builder image
|
||||
COPY --from=builder --chown=myuser /home/myuser/dist ./dist
|
||||
|
||||
# Copy just package.json and package-lock.json
|
||||
# to speed up the build using Docker layer cache.
|
||||
COPY --chown=myuser package*.json ./
|
||||
|
||||
# Ensure we'll install Camoufox using the npm postinstall script
|
||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0
|
||||
# Install NPM packages, skip optional and development dependencies to
|
||||
# keep the image small. Avoid logging too much and print the dependency
|
||||
# tree for debugging
|
||||
RUN npm --quiet set progress=false \
|
||||
&& npm install --omit=dev \
|
||||
&& echo "Installed NPM packages:" \
|
||||
&& (npm list --omit=dev --all || true) \
|
||||
&& echo "Node.js version:" \
|
||||
&& node --version \
|
||||
&& echo "NPM version:" \
|
||||
&& npm --version
|
||||
|
||||
# Next, copy the remaining files and directories with the source code.
|
||||
# Since we do this after NPM install, quick build will be really fast
|
||||
# for most source file changes.
|
||||
COPY --chown=myuser . ./
|
||||
|
||||
# Run the image. If you know you won't need headful browsers,
|
||||
# you can remove the XVFB start script for a micro perf gain.
|
||||
CMD ./start_xvfb_and_run_cmd.sh && npm run start:prod --silent
|
||||
8
job-extractor/README.md
Normal file
8
job-extractor/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Crawlee + PlaywrightCrawler + Camoufox + TypeScript project
|
||||
|
||||
This template is a production ready boilerplate for developing with `PlaywrightCrawler`. Use this to bootstrap your projects using the most up-to-date code.
|
||||
|
||||
If you're looking for examples or want to learn more visit:
|
||||
|
||||
- [Documentation](https://crawlee.dev/js/api/playwright-crawler/class/PlaywrightCrawler)
|
||||
- [Examples](https://crawlee.dev/js/docs/examples/playwright-crawler)
|
||||
4941
job-extractor/package-lock.json
generated
Normal file
4941
job-extractor/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
job-extractor/package.json
Normal file
30
job-extractor/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "job-flow",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"description": "This is an example of a Crawlee project.",
|
||||
"dependencies": {
|
||||
"camoufox-js": "^0.8.0",
|
||||
"crawlee": "^3.0.0",
|
||||
"playwright": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apify/tsconfig": "^0.1.0",
|
||||
"@types/fs-extra": "^11",
|
||||
"@types/node": "^24.0.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"tsx": "^4.4.0",
|
||||
"typescript": "~5.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:dev",
|
||||
"start:prod": "node dist/main.js",
|
||||
"start:dev": "tsx src/main.ts",
|
||||
"build": "tsc",
|
||||
"test": "echo \"Error: oops, the actor has no tests yet, sad!\" && exit 1",
|
||||
"get-binaries": "camoufox-js fetch",
|
||||
"postinstall": "npm run get-binaries"
|
||||
},
|
||||
"author": "It's not you it's me",
|
||||
"license": "ISC"
|
||||
}
|
||||
43
job-extractor/src/main.ts
Normal file
43
job-extractor/src/main.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// For more information, see https://crawlee.dev/
|
||||
import { launchOptions } from "camoufox-js";
|
||||
import { PlaywrightCrawler, ProxyConfiguration } from "crawlee";
|
||||
import { firefox } from "playwright";
|
||||
|
||||
import { router } from "./routes.js";
|
||||
|
||||
const startUrls = [
|
||||
{
|
||||
url: "https://www.gradcracker.com/search/computing-technology/web-development-graduate-jobs-in-north-west?order=dateAdded",
|
||||
userData: { label: "gradcracker-list-page" },
|
||||
},
|
||||
];
|
||||
|
||||
const crawler = new PlaywrightCrawler({
|
||||
// proxyConfiguration: new ProxyConfiguration({ proxyUrls: ['...'] }),
|
||||
requestHandler: router,
|
||||
// Comment this option to scrape the full website.
|
||||
maxRequestsPerCrawl: 20,
|
||||
// Add delay between requests to slow down the process
|
||||
minConcurrency: 1,
|
||||
maxConcurrency: 2,
|
||||
navigationTimeoutSecs: 60,
|
||||
// Add delay between requests (in milliseconds)
|
||||
// requestHandlerTimeoutSecs: 50,
|
||||
browserPoolOptions: {
|
||||
// Disable the default fingerprint spoofing to avoid conflicts with Camoufox.
|
||||
useFingerprints: false,
|
||||
},
|
||||
launchContext: {
|
||||
launcher: firefox,
|
||||
launchOptions: await launchOptions({
|
||||
headless: false,
|
||||
// block_images: true,
|
||||
// Pass your own Camoufox parameters here...
|
||||
// block_images: true,
|
||||
// fonts: ['Times New Roman'],
|
||||
// ...
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
await crawler.run(startUrls);
|
||||
180
job-extractor/src/routes.ts
Normal file
180
job-extractor/src/routes.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import { createPlaywrightRouter, log } from "crawlee";
|
||||
|
||||
interface Job {
|
||||
title: string | null;
|
||||
jobUrl: string | null;
|
||||
employer: string | null;
|
||||
employerUrl: string | null;
|
||||
disciplines: string | null;
|
||||
deadline: string | null;
|
||||
salary: string | null;
|
||||
location: string | null;
|
||||
degreeRequired: string | null;
|
||||
starting: string | null;
|
||||
}
|
||||
|
||||
export const router = createPlaywrightRouter();
|
||||
|
||||
router.addHandler(
|
||||
"gradcracker-list-page",
|
||||
async ({ page, request, enqueueLinks }) => {
|
||||
log.info(`Processing: ${request.url}`);
|
||||
|
||||
// Wait until the job cards are rendered
|
||||
await page.waitForSelector("article[wire\\:key]", { timeout: 30000 });
|
||||
|
||||
// Add delay to see the page load
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
const toAbsolute = (href: string | null) => {
|
||||
if (!href) return null;
|
||||
try {
|
||||
return new URL(href, request.loadedUrl).href;
|
||||
} catch {
|
||||
return href;
|
||||
}
|
||||
};
|
||||
|
||||
const articles = await page.locator("article[wire\\:key]").all();
|
||||
const jobs: Job[] = [];
|
||||
|
||||
for (const article of articles) {
|
||||
const titleLocator = article.locator("h2 a");
|
||||
const title = (await titleLocator.textContent())?.trim() ?? null;
|
||||
const jobUrl = toAbsolute(await titleLocator.getAttribute("href"));
|
||||
|
||||
const employerImg = article.locator("figure img");
|
||||
const employer = (await employerImg.getAttribute("alt"))?.trim() ?? null;
|
||||
|
||||
const employerAnchor = article.locator("figure a");
|
||||
const employerUrl = toAbsolute(await employerAnchor.getAttribute("href"));
|
||||
|
||||
const disciplinesEl = article.locator("h3");
|
||||
const disciplines = (await disciplinesEl.textContent())?.trim() ?? null;
|
||||
|
||||
// Find the "Deadline: ..." pill
|
||||
const deadlineLocator = article
|
||||
.locator("div", { hasText: "Deadline:" })
|
||||
.first();
|
||||
let deadline: string | null = null;
|
||||
if ((await deadlineLocator.count()) > 0) {
|
||||
const deadlineText = (await deadlineLocator.textContent()) ?? "";
|
||||
// Extract deadline and clean up whitespace
|
||||
deadline =
|
||||
deadlineText
|
||||
.replace("Deadline:", "")
|
||||
.split("\n")[0] // Take only first line
|
||||
.trim() || null;
|
||||
}
|
||||
|
||||
const getDdText = async (label: string) => {
|
||||
// Find dt that has the exact label text (ignoring whitespace)
|
||||
const dt = article
|
||||
.locator("dt")
|
||||
.filter({ hasText: new RegExp(`^\\s*${label}\\s*$`) });
|
||||
if ((await dt.count()) === 0) return null;
|
||||
|
||||
// Get the next sibling dd
|
||||
const dd = dt.locator("+ dd");
|
||||
if ((await dd.count()) > 0) {
|
||||
const text = await dd.textContent();
|
||||
if (!text) return null;
|
||||
// Clean up: remove extra whitespace and newlines
|
||||
return text.replace(/\s+/g, " ").trim() || null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const salary = await getDdText("Salary");
|
||||
const location = await getDdText("Location");
|
||||
const degreeRequired = await getDdText("Degree required");
|
||||
const starting = await getDdText("Starting");
|
||||
|
||||
jobs.push({
|
||||
title,
|
||||
jobUrl,
|
||||
employer,
|
||||
employerUrl,
|
||||
disciplines,
|
||||
deadline,
|
||||
salary,
|
||||
location,
|
||||
degreeRequired,
|
||||
starting,
|
||||
});
|
||||
|
||||
// append more links to crawl: single job pages
|
||||
if (jobUrl) {
|
||||
await enqueueLinks({
|
||||
urls: [jobUrl],
|
||||
userData: {
|
||||
...jobs[jobs.length - 1],
|
||||
label: "gradcracker-single-job-page"
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
log.info(`Extracted ${jobs.length} jobs`);
|
||||
}
|
||||
);
|
||||
|
||||
router.addHandler(
|
||||
"gradcracker-single-job-page",
|
||||
async ({ page, request, pushData, log }) => {
|
||||
const { label, ...jobSummary } = request.userData;
|
||||
log.info(`Processing single job page: ${request.url}`);
|
||||
|
||||
// Wait for job content to be present
|
||||
await page.waitForSelector(".body-content", { timeout: 30000 });
|
||||
|
||||
// Optional delay if you want to visually see it while debugging
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const jobDescription =
|
||||
(await page.locator(".body-content").textContent())?.trim() || null;
|
||||
|
||||
const applyButton = page.locator('a[dusk="apply-button"]');
|
||||
const hasApplyButton = (await applyButton.count()) > 0;
|
||||
|
||||
let applicationLink: string | null = null;
|
||||
|
||||
if (hasApplyButton) {
|
||||
const context = page.context();
|
||||
const originalUrl = page.url();
|
||||
|
||||
// Race the click with "a new page opened" event.
|
||||
// If it opens in the same tab, waitForEvent('page') will just time out and we fallback.
|
||||
const [maybeNewPage] = await Promise.all([
|
||||
context.waitForEvent("page").catch(() => null),
|
||||
applyButton.click(),
|
||||
]);
|
||||
|
||||
// If a new tab/window opened, use that; otherwise stay on the current page.
|
||||
const targetPage = maybeNewPage ?? page;
|
||||
|
||||
// Wait for whatever we landed on to finish loading a bit
|
||||
await targetPage.waitForTimeout(7500);
|
||||
|
||||
applicationLink = targetPage.url();
|
||||
|
||||
// Optional sanity check: if the URL is still the original Gradcracker page,
|
||||
// you can decide to treat it as "no external app link found".
|
||||
if (applicationLink === originalUrl) {
|
||||
log.info(
|
||||
`Apply click did not change URL (still Gradcracker): ${applicationLink}`
|
||||
);
|
||||
} else {
|
||||
log.info(`Captured application URL: ${applicationLink}`);
|
||||
}
|
||||
} else {
|
||||
log.warning(`Apply button not found on page: ${request.url}`);
|
||||
}
|
||||
|
||||
await pushData({
|
||||
...jobSummary,
|
||||
url: request.url, // Gradcracker job page
|
||||
applicationLink, // External or same-page URL after click
|
||||
jobDescription,
|
||||
});
|
||||
}
|
||||
);
|
||||
12
job-extractor/tsconfig.json
Normal file
12
job-extractor/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@apify/tsconfig",
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"target": "ES2022",
|
||||
"outDir": "dist",
|
||||
"noUnusedLocals": false,
|
||||
"lib": ["DOM"]
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
661
resume-generator/base.json
Normal file
661
resume-generator/base.json
Normal file
@ -0,0 +1,661 @@
|
||||
{
|
||||
"basics": {
|
||||
"name": "Shaheer Sarfaraz",
|
||||
"headline": "Frontend Software Engineer (React/TypeScript) \u00b7 Autodesk Intern \u00b7 Open Source & Product Work",
|
||||
"email": "shaheer30sarfaraz@gmail.com",
|
||||
"phone": "+44 7359 501592",
|
||||
"location": "Blackpool, United Kingdom",
|
||||
"url": {
|
||||
"label": "https://dakheera47.com/",
|
||||
"href": "https://dakheera47.com/"
|
||||
},
|
||||
"customFields": [],
|
||||
"picture": {
|
||||
"url": "",
|
||||
"size": 120,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 0,
|
||||
"effects": {
|
||||
"hidden": false,
|
||||
"border": false,
|
||||
"grayscale": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"summary": {
|
||||
"name": "Summary",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "summary",
|
||||
"content": "<blockquote><p>I\u2019m a BSc (Hons) Computer Science student at the University of Lancashire, graduating in June 2026 with a First-class average, and I\u2019ve spent a year as a Software Engineering Intern at Autodesk working in a large React/TypeScript production codebase. I\u2019m comfortable using Python for scripting, data cleaning, and small backend services, and I have academic experience with SQL from my databases module, which I\u2019ve applied in analytics-focused side projects. I\u2019m particularly interested in AI-driven systems and would be excited to help develop and improve AI agents for marketing and user acquisition while working closely with data scientists, engineers, and marketing/product teams</p></blockquote>"
|
||||
},
|
||||
"awards": {
|
||||
"name": "Awards",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "awards",
|
||||
"items": []
|
||||
},
|
||||
"certifications": {
|
||||
"name": "Certifications",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "certifications",
|
||||
"items": []
|
||||
},
|
||||
"education": {
|
||||
"name": "Education",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "education",
|
||||
"items": [
|
||||
{
|
||||
"id": "yo3p200zo45c6cdqc6a2vtt3",
|
||||
"visible": true,
|
||||
"institution": "University of Lancashire",
|
||||
"studyType": "BSc (Hons) Computer Science",
|
||||
"area": "Preston, United Kingdom",
|
||||
"score": "1st Class",
|
||||
"date": "September 2022 to June 2026",
|
||||
"summary": "<p style=\"text-align: left;\">Relevant Modules: Web Applications, Algorithms & Data Structures, Game Development, Databases, Software Engineering (Agile group project)</p>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://www.lancashire.ac.uk/undergraduate/courses/computer-science-bsc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ei2fvjokusg3cfmdyolmgcoz",
|
||||
"visible": false,
|
||||
"institution": " ",
|
||||
"studyType": "",
|
||||
"area": "A Levels",
|
||||
"score": "",
|
||||
"date": "",
|
||||
"summary": "<ul><li><p>Maths: A</p></li><li><p>Computer Science: B</p></li><li><p>Physics: C</p></li><li><p>Chemistry: E</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "pm4r5hngvv1w4mc79o22irfx",
|
||||
"visible": false,
|
||||
"institution": " ",
|
||||
"studyType": "",
|
||||
"area": "GCSEs",
|
||||
"score": "",
|
||||
"date": "",
|
||||
"summary": "<ol><li><p>English: A*</p></li><li><p>Computer Science: A*</p></li><li><p>Urdu: A</p></li><li><p>Islamiat: A</p></li><li><p>Pakistan Studies: A</p></li><li><p>Biology: A</p></li><li><p>Chemistry: A</p></li><li><p>Physics: A</p></li><li><p>Maths: A</p></li></ol>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"experience": {
|
||||
"name": "Experience",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "experience",
|
||||
"items": [
|
||||
{
|
||||
"id": "ng9ui2azk7w4y8oyu8kazqeb",
|
||||
"visible": true,
|
||||
"company": "Autodesk",
|
||||
"position": "Software Engineering Intern",
|
||||
"location": "Hybrid (Sheffield Based)",
|
||||
"date": "July 2024 - June 2025",
|
||||
"summary": "<ul><li><p><strong>Implemented front-end features and fixes</strong> in the Autodesk Construction Cloud Model Coordination app, working in a ~10-year-old React/JavaScript/TypeScript codebase (7k+ commits) using Webpack module federation and Autodesk\u2019s Exoskeleton dev environment</p></li><li><p>Improved reliability of the <strong>Cypress end-to-end test suite</strong> by diagnosing flaky tests, adding new E2E coverage, and participating in focused \u201ctest fest\u201d events ahead of major feature releases</p></li><li><p>Collaborated with cross-functional teams (like the Design System, platform teams) by <strong>raising well-scoped bugs</strong>, augmenting existing tickets with reproduction steps and context, and aligning on shared component and API changes</p></li><li><p>Helped strengthen team processes by <strong>running weekly stand-ups</strong> and retrospectives, organising a ticket-scoping meeting, and <strong>participating in technical reviews & ADR discussions</strong> (e.g. standardising error handling and planning clash data streaming)</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "lhw25d7gf32wgdfpsktf6e0x",
|
||||
"visible": true,
|
||||
"company": "Mirage",
|
||||
"position": "Co-Founder & Lead Developer",
|
||||
"location": "",
|
||||
"date": "December 2019 to Present",
|
||||
"summary": "<ul><li><p>Delivered <strong>10+ production websites and webapps</strong> for small and medium size clients (e.g. Indus Marine Services, Mumtaz Urdu), from initial scoping to deployment and handover</p></li><li><p>Built with <strong>modern web stacks</strong> (Next.js, Node/Express, Tailwind, Strapi, WordPress/Elementor where appropriate), setting up CI/CD and hosting</p></li><li><p><strong>Led a small team of four developers</strong>, handling code reviews, task breakdown, and client communication</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://promirage.com/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k6zxqunkb225hbjso3c3vykk",
|
||||
"visible": true,
|
||||
"company": "University of Lancashire",
|
||||
"position": "Computing Student Mentor",
|
||||
"location": "Preston, UK",
|
||||
"date": "July 2023 - July 2024",
|
||||
"summary": "<ul><li><p><strong>Academic Support and Leadership:</strong> Provided academic guidance to over 10 first-year students once a week, significantly enhancing their understanding and skills in key subjects like programming and web development.</p></li><li><p style=\"text-align: start\"><strong>Collaborative Learning Environment:</strong> Actively fostered a collaborative and supportive learning environment for a group of 10 students. This role also honed my leadership and communication skills, facilitating better academic outcomes for mentees.</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a1bg5d8gp8sulf91xzdcsiaq",
|
||||
"visible": true,
|
||||
"company": "Research and Knowledge Exchange Institute",
|
||||
"position": "Undergraduate Research Intern (HCI & EdTech)",
|
||||
"location": "",
|
||||
"date": "Summer 2024",
|
||||
"summary": "<ul><li><p>Built a <strong>mouse \u201ctorch-reveal\u201d web app</strong> (<strong>Astro</strong>) to approximate eye-tracking; ran on-campus studies with Revoe Learning Academy pupils\u2014<strong>1</strong> eye-tracked, <strong>9</strong> using my app.</p></li><li><p>Logged cursor paths, dwell time, and reveal order; delivered setup notes for staff to run sessions independently.</p></li><li><p>Developed a <strong>Questionnaire Randomiser</strong> (Next.js): selectable response metrics (<strong>smileys / numbers / stars</strong>), configurable randomisation strategies, and <strong>ZIP export of per-student PDFs</strong> ready for print.</p></li><li><p>Extras: lightweight analytics for comparison with the eye-tracking baseline; optional CSV/JSON data export.</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tx32suzrg2bs5eumcbjei4ns",
|
||||
"visible": false,
|
||||
"company": "University of Lancashire",
|
||||
"position": "Student Ambassador",
|
||||
"location": "Preston, UK",
|
||||
"date": "July 2023 - Present",
|
||||
"summary": "<ul><li><p><strong>Diverse Role Engagement:</strong> Actively engaged in various tasks, from guiding tours to assisting on open days, demonstrating adaptability and organizational skills.</p></li><li><p><strong>Campus Culture Promotion:</strong> Contributed to enhancing the university\u2019s inclusive campus atmosphere, showcasing the university's vibrant community to prospective students.</p></li></ul>",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"volunteer": {
|
||||
"name": "Volunteering",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "volunteer",
|
||||
"items": []
|
||||
},
|
||||
"interests": {
|
||||
"name": "Interests",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": false,
|
||||
"id": "interests",
|
||||
"items": []
|
||||
},
|
||||
"languages": {
|
||||
"name": "Languages",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "languages",
|
||||
"items": []
|
||||
},
|
||||
"profiles": {
|
||||
"name": "Profiles",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "profiles",
|
||||
"items": [
|
||||
{
|
||||
"id": "ukl0uecvzkgm27mlye0wazlb",
|
||||
"visible": true,
|
||||
"network": "GitHub",
|
||||
"username": "DaKheera47",
|
||||
"icon": "github",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://github.com/DaKheera47"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cnbk5f0aeqvhx69ebk7hktwd",
|
||||
"visible": true,
|
||||
"network": "LinkedIn",
|
||||
"username": "ssarfaraz30",
|
||||
"icon": "linkedin",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://www.linkedin.com/in/ssarfaraz30/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "linnyxv78zdep1xwirpa2ia1",
|
||||
"visible": true,
|
||||
"network": "Hashnode",
|
||||
"username": "DaKheera47",
|
||||
"icon": "hashnode",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://dakheera47.hashnode.dev/"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"name": "Projects",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "projects",
|
||||
"items": [
|
||||
{
|
||||
"id": "yw843emozcth8s1ubi1ubvlf",
|
||||
"visible": false,
|
||||
"name": "Atoro",
|
||||
"description": "Lead Developer",
|
||||
"date": "January 2023",
|
||||
"summary": "<ol><li><p><strong>Next.js Implementation for Enhanced SEO:</strong> Utilized Next.js to optimize the website for search engines, significantly improving its online visibility and user engagement.</p></li><li><p><strong>Strapi Backend Integration:</strong> Streamlined content management by implementing a Strapi backend, enhancing the efficiency and scalability of the website's content updates.</p></li><li><p><strong>Responsive Design with Tailwind CSS:</strong> Employed Tailwind CSS for a utility-first approach, ensuring the website's responsiveness and seamless user experience across various devices.</p></li><li><p><strong>Continuous Deployment Pipeline Establishment:</strong> Developed a continuous deployment pipeline, ensuring real-time updates and maintaining high performance and reliability of the website.</p></li><li><p><strong>Optimized Web Performance:</strong> Focused on optimizing web performance by efficiently loading images and managing JavaScript bundles, leading to a faster and more efficient user experience.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://atoro.promirage.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ncxgdjjky54gh59iz2t1xi1v",
|
||||
"visible": false,
|
||||
"name": "Stellar Consultancy",
|
||||
"description": "Lead Developer",
|
||||
"date": "April 2023",
|
||||
"summary": "<ol><li><p><strong>WordPress and Elementor Integration:</strong> Expertly utilized WordPress with Elementor to build a robust content management system, enhancing the website's scalability and user interaction capabilities.</p></li><li><p><strong>Client Engagement and Trust Building:</strong> Implemented features to showcase client testimonials, effectively building trust and displaying the success of previous project engagements.</p></li><li><p><strong>Intuitive Design and User Engagement:</strong> Focused on intuitive page design and structuring, streamlining site maintenance and content updates, thereby enhancing user engagement.</p></li><li><p><strong>Effective Call-to-Actions:</strong> Crafted clear call-to-actions and provided essential contact information, significantly improving user interaction and conversion rates.</p></li><li><p><strong>Portfolio Display for Business Showcase:</strong> Presented past work and services offered through a comprehensive portfolio display, allowing visitors to assess the quality and impact of Stellar Consultancy's services.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stellarconsultancy.ca"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tcecguinuctb8mu2xqrn97m8",
|
||||
"visible": true,
|
||||
"name": "Mumtaz Urdu",
|
||||
"description": "Developer",
|
||||
"date": "July 2022",
|
||||
"summary": "<ol><li><p><strong>Server-Rendered Web Application Development</strong>: Created the Mumtaz Urdu platform with Next.js to optimize server-side rendering for enhanced SEO and performance.</p></li><li><p><strong>UI Development with Tailwind CSS</strong>: Implemented utility-first Tailwind CSS, ensuring rapid, responsive design for a seamless user interface.</p></li><li><p><strong>Scalable Storage Solution</strong>: Integrated scalable Amazon S3 storage, supporting the application's growth and robust data management.</p></li><li><p><strong>Progressive Web App Implementation</strong>: Developed PWA features for Mumtaz Urdu, offering users native-like mobile access and increased engagement.</p></li><li><p><strong>High Traffic Data Management</strong>: Engineered Mumtaz Urdu's backend with Next.js and MongoDB, enabling the handling and efficient processing of vast amounts of user data for thousands of monthly users.</p></li><li><p><strong>Test-Driven Development</strong>: Embraced TDD practices to ensure reliable and high-quality code, facilitating regular testing throughout the development process for continuous improvement.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://www.mumtazurdu.com/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "to47h749kaj6t02j3f9kprxq",
|
||||
"visible": false,
|
||||
"name": "PyScreeze",
|
||||
"description": "Open Source Contribution",
|
||||
"date": "January 2022",
|
||||
"summary": "<ol><li><p><strong>Innovative Feature Implementation:</strong> Implemented the <code>locateCenterOnScreenNear</code> function for <code>PyScreeze</code>, enhancing the library's functionality by enabling precise image location near a specified point on the screen.</p></li><li><p><strong>Open Source Contribution:</strong> Marked my debut in open-source contributions with this significant addition to <code>PyScreeze</code>, showcasing my initiative and ability to contribute effectively to community-driven projects.</p></li><li><p><strong>Collaborative Development and Recognition:</strong> Collaborated with the project's maintainer, <code>asweigart</code>, to refine and integrate the function into the main codebase, receiving recognition for this valuable contribution to the project.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://github.com/asweigart/pyscreeze/pull/79"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gt7yq82ulor5hmmutdhuvfo1",
|
||||
"visible": false,
|
||||
"name": "Threegency",
|
||||
"description": "Lead Developer",
|
||||
"date": "February 2023",
|
||||
"summary": "<ul><li><p><strong>Framework</strong>: Utilized Next.js to build a server-rendered React website, enhancing SEO and ensuring optimal performance.</p></li><li><p><strong>Styling</strong>: Employed Tailwind CSS for utility-first styling, facilitating rapid UI development.</p></li><li><p><strong>Content Management</strong>: Leveraged Strapi as a CMS, enabling streamlined content updates and administration.</p></li><li><p><strong>Data Handling</strong>: Utilized GraphQL for data handling, ensuring efficient and flexible data retrieval.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://www.threegency.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c8fcu3nz541a4d5zcurx6b8c",
|
||||
"visible": false,
|
||||
"name": "AutoClass",
|
||||
"description": "GUI Automation",
|
||||
"date": "November 2021",
|
||||
"summary": "<ul><li><p><strong>Framework</strong>: Written in Python, leveraging the versatility and ease-of-use of the language.</p></li><li><p><strong>Automation Library</strong>: Utilized PyAutoGUI for automating user interactions, enhancing the utility of the application.</p></li><li><p><strong>Iterative Improvement</strong>: Progressively refined over a year, demonstrating a commitment to robustness and reliability.</p></li><li><p><strong>Project Purpose</strong>: Developed to automate the process of joining Zoom classes, alleviating the repetitive morning routine.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://github.com/DaKheera47/autoclass"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rv23bgibq6bye6rujmcx1ygc",
|
||||
"visible": false,
|
||||
"name": "Meet Link Generator",
|
||||
"description": "GUI Automation",
|
||||
"date": "January 2022",
|
||||
"summary": "<ul><li><p><strong>Functionality</strong>: Generates Google Meet links with specific words in the URL by brute-forcing the creation of thousands of links until the desired pattern is achieved. Doing so enables creation of Google Meet links with specific codes or phrases.</p></li><li><p><strong>Optimized Automation</strong>: The final product uses Python with PyAutoGUI for efficient and rapid creation of new Google Meet links.</p></li><li><p><strong>Speed and Efficiency</strong>: Drastically improved performance, finally achieving the link generation time to under 1 second per link, limited only by internet speed.</p></li><li><p><strong>Interface Interaction</strong>: Utilizes the Google Meet homepage's features for quicker link generation, avoiding full page refreshes for speed.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://github.com/DaKheera47/meet-link-generator"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tu98rghbi5c43ogget5mh7ih",
|
||||
"visible": false,
|
||||
"name": "UCLan Server-side Web Application Project",
|
||||
"description": "",
|
||||
"date": "UCLan Year 1",
|
||||
"summary": "<ul><li><p><strong>Backend Development with PHP and MySQL:</strong> Developed the backend for a Student\u2019s Union Shop web application, integrating PHP and MySQL for dynamic data handling and backend database communication.</p></li><li><p><strong>User Authentication and Session Management:</strong> Implemented user sign-up and login functionality using PHP sessions, enabling secure and personalized shopping experiences.</p></li><li><p><strong>Dynamic Content Display from Database:</strong> Enhanced the application to dynamically display products and offers directly from the database, moving away from static HTML content.</p></li><li><p><strong>Advanced Search and Personalization Features:</strong> Integrated advanced product search capabilities and personalized user greetings, improving user interactivity and engagement.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ov4lkbc1vl169ynfnj91m1lm",
|
||||
"visible": false,
|
||||
"name": "Square About",
|
||||
"description": "",
|
||||
"date": "UCLan Year 1",
|
||||
"summary": "<ul><li><p><strong>Advanced 3D Game Development:</strong> Implemented a complex 3D game using TL-Engine, featuring intricate gameplay mechanics and immersive 3D visuals.</p></li><li><p><strong>Dynamic Gameplay Elements:</strong> Integrated multiple spheres with varying behaviors, including super-spheres requiring multiple hits, enhancing the game's challenge and engagement levels.</p></li><li><p><strong>Interactive Game Controls:</strong> Developed features for speed control and directional change, allowing players to interact dynamically with the game environment.</p></li><li><p><strong>Strategic Game Mechanics:</strong> Added a bullet firing mechanism with a limited ammo concept, introducing strategic elements and a scoring system to the gameplay.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "s3r37gdr0oa84a6dp6r5nl58",
|
||||
"visible": false,
|
||||
"name": "Car Smash",
|
||||
"description": "",
|
||||
"date": "UCLan Year 1",
|
||||
"summary": "<ol><li><p><strong>3D Car Smash Game Development:</strong> Developed a 3D car smash game using TL-Engine, showcasing skills in game engine utilization and 3D gaming.</p></li><li><p><strong>Collision Detection Mechanics:</strong> Implemented advanced collision detection between player's car and enemy vehicles, enhancing gameplay realism.</p></li><li><p><strong>Dynamic Game States and Camera Views:</strong> Integrated multiple game states and camera views, including a chase camera and first-person view, for an immersive gaming experience.</p></li><li><p><strong>Enhanced Player Interaction:</strong> Created a more realistic driving experience with accelerated movement and bounce effects on collisions, and introduced particle systems for visual effects.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gylzkvl103m9s7ywag4xpdy4",
|
||||
"visible": false,
|
||||
"name": "Tweet Filter",
|
||||
"description": "",
|
||||
"date": "UCLan Year 1",
|
||||
"summary": "<ol><li><p><strong>Tweet Filtration System:</strong> Crafted a C++ program to filter out prohibited words from tweets, showcasing text processing and file handling capabilities.</p></li><li><p><strong>Advanced Text Manipulation:</strong> Enhanced the program to filter varying cases and contexts of banned words, even within larger strings, demonstrating attention to detail in string operations.</p></li><li><p><strong>Output Generation:</strong> Implemented functionality to write filtered tweets to new files, maintaining data integrity and displaying proficiency in file I/O operations.</p></li><li><p><strong>Algorithm Optimization:</strong> Utilized data structures like vectors and implemented mathematical techniques for efficient word frequency analysis and sentiment determination.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enav754zxhuc9uycbb83s94q",
|
||||
"visible": false,
|
||||
"name": "Burger Ordering App",
|
||||
"description": "",
|
||||
"date": "UCLan Year 1",
|
||||
"summary": "<ol><li><p><strong>Interactive Console Application:</strong> Engineered a C++ console application simulating a burger ordering process, highlighting proficiency in creating user-interactive software.</p></li><li><p><strong>Complex Logic Implementation:</strong> Designed and implemented complex logic for burger size and topping selection, including pricing and order summary features.</p></li><li><p><strong>Data Handling and User Input:</strong> Developed robust credit system and user input validation for an intuitive ordering experience, showcasing attention to detail and user-centric design.</p></li><li><p><strong>Readable and Maintainable Code:</strong> Produced well-documented, maintainable code with clear variable naming and structured formatting, demonstrating best practices in software development.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "hl6jgeswr01tlul3iwoat05d",
|
||||
"visible": false,
|
||||
"name": "LinkLander",
|
||||
"description": "Android Studio, Kotlin",
|
||||
"date": "December 2023 - Ongoing",
|
||||
"summary": "<ul><li><p><strong>Innovative Android Utility:</strong> Developed LinkLander, a Kotlin-based Android application that simplifies the process of downloading online content directly to devices.</p></li><li><p><strong>User-Centric Design:</strong> Focused on addressing Android system limitations by providing a seamless shortcut for redirecting links to an online video downloading service.</p></li><li><p><strong>Simplicity and Efficiency:</strong> Emphasized a user-friendly interface, enhancing the Android experience by streamlining content downloads.</p></li><li><p><strong>Technical Proficiency in Kotlin:</strong> Leveraged the capabilities of Kotlin for Android development to create a practical solution for niche digital tasks.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "v4s0ljbiiio198y8l1wl0ym6",
|
||||
"visible": false,
|
||||
"name": "AR App Development with AGILE",
|
||||
"description": "Unity, C#",
|
||||
"date": "October 2023 - Ongoing",
|
||||
"summary": "<ul><li><p><strong>Agile Development in Action</strong>: Participated in an Agile team project, developing an AR application for supporting disabled students with a team of five, demonstrating an application of Agile methodologies in a real-world scenario.</p></li><li><p><strong>Mobile AR Application Prototype</strong>: Developed a proof-of-concept prototype using Unity and C# for mobile platforms, showcasing technical skills in modern app development environments.</p></li><li><p><strong>Collaborative Software Engineering</strong>: Engaged in a collaborative environment, contributing code and ideas, emphasizing teamwork and shared responsibility in software creation.</p></li><li><p><strong>Presentation and Critical Analysis</strong>: Delivered a comprehensive presentation and critical report, evaluating the Agile process, product development, and personal learning outcomes.</p></li></ul>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fwxrq682hqrj1y76rmziqrbk",
|
||||
"visible": true,
|
||||
"name": "Indus Marine Services",
|
||||
"description": "System Design & Development",
|
||||
"date": "May 2022 - Ongoing",
|
||||
"summary": "<ol><li><p><strong>Induction System for Marine Services</strong>: Designed & developed an induction system for Indus Marine Services in the UAE, streamlining the employee onboarding process with interactive testing and certification issuance.</p></li><li><p><strong>Admin-Centric Functionality</strong>: Devised a back-end system allowing admins to oversee inductee progress, manage documents, and curate customized quizzes as per requirements</p></li><li><p><strong>Client Engagement Interface</strong>: Implemented a user-friendly front-end where inductees receive personalized email prompts, complete quizzes, and obtain certifications, all contributing to a seamless induction experience.</p></li><li><p><strong>Robust Tech Stack Integration</strong>: Utilized a sophisticated stack comprising Node.js, Express, EJS, and Tailwind CSS to build a responsive, scalable, and easily navigable system.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "http://www.ims-auh.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "jdfyaez8vq1b7xfr9rmxmz06",
|
||||
"visible": false,
|
||||
"name": "VECTOR AI",
|
||||
"description": "Website Development",
|
||||
"date": "February 2024 - February 2024",
|
||||
"summary": "<ol><li><p><strong>Innovative AI Development</strong>: As the driving force behind VECTOR's website development, I spearheaded the technical design using Astro, with a cutting-edge stack including React and Tailwind CSS.</p></li><li><p><strong>Data-Driven Content Strategy</strong>: Leveraged Astro content management capabilities to structure and present data, ensuring content is dynamic, easily accessible, and optimized for both performance and scalability.</p></li><li><p><strong>Astro for Enhanced Performance</strong>: Utilized Astro for static site generation, making VECTOR's website performance fast for a pleasant user experience</p></li><li><p><strong>React for Responsive Interaction</strong>: Utilized React\u2019s robust ecosystem to develop interactive elements, ensuring that each module of VECTOR\u2019s platform is engaging and seamless for users across various touchpoints.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://vector-ai.co/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "qdhmfkqpfql19ohfas1g91ek",
|
||||
"visible": false,
|
||||
"name": "UCLan's First Hackathon",
|
||||
"description": "Hackathon, Team Work",
|
||||
"date": "February 2024",
|
||||
"summary": "<ol><li><p><strong>Second Place in UCLan Hackathon</strong>: Earned second place in UCLan's first hackathon by developing an app to simplify university life. Focused on enhancing the attendance monitoring process for student mentors.</p></li><li><p><strong>TRPC for End-to-End Type Safety</strong>: Utilized TRPC to ensure end-to-end type safety, enhancing the app's reliability and streamlining the development process.</p></li><li><p><strong>Supabase Backend Integration</strong>: Implemented Supabase as a backend solution, providing a robust and scalable database for managing attendance data efficiently.</p></li><li><p><strong>Amazon SES and OAuth Integration</strong>: Integrated Amazon SES for email notifications and OAuth for secure Google login, improving user experience and communication.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw3x7tapntrt877rbl4pnxz7",
|
||||
"visible": true,
|
||||
"name": "NASA Space Apps Challenge",
|
||||
"description": "A 48-hour, global hackathon powered by NASA open data",
|
||||
"date": "Oct 4\u20135, 2025",
|
||||
"summary": "<ol><li><p><strong>Full-Stack Integration:</strong> Wired up backend services to a responsive frontend, enabling real-time exploration of <strong>Kepler/K2/TESS</strong> catalogs and smooth model-scoring UX.</p></li><li><p><strong>Data Harmonization Pipeline:</strong> Cleaned, merged, and standardized multi-mission catalogs into a unified schema, unblocking ML teammates and cutting data-prep time by <strong>60%+</strong> during the hack.</p></li><li><p><strong>Analytics UI & Upload Flow:</strong> Built an upload \u2192 validate \u2192 score workflow and a clear results dashboard so researchers can triage candidates in minutes, not hours.</p></li><li><p><strong>Delivery Under Pressure:</strong> Coordinated a <strong>5-person</strong> multidisciplinary team to ship a working web app in <strong>48 hours</strong>, with demo-ready reliability for judging.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://exploranium.vercel.app/dashboard"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "i2t6epmx5v7s0d8rqtxsigp3",
|
||||
"visible": true,
|
||||
"name": "Strong Statistics",
|
||||
"description": "Self-hosted strength analytics app using FastAPI and Next.js to visualize Strong app data with full local privacy and active open-source adoption.",
|
||||
"date": "September 2025 - Present",
|
||||
"summary": "<ol><li><p><strong>Self-Hosted Strength Analytics Platform:</strong> Developed <em>strong-statistics</em>, an open-source web app that visualizes detailed workout analytics from the <strong>Strong </strong>and<strong> Hevy</strong> fitness app, giving users local control of their training data.</p></li><li><p><strong>Full-Stack Architecture:</strong> Built a modular stack with <strong>FastAPI</strong>, <strong>Next.js</strong>, <strong>Tailwind CSS</strong>, and <strong>SQLite</strong>, deployed via <strong>Docker Compose</strong> for seamless self-hosting and persistent local data storage.</p></li><li><p><strong>Active Open-Source Ecosystem:</strong> Published on GitHub with <strong>community engagement from global users</strong> \u2014 external contributors opened feature requests and bug reports, validating real-world adoption and reliability.</p></li><li><p><strong>Continuous Personal Use & Maintenance:</strong> Regularly updated and used in live deployment at <a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"http://lifting.dakheera47.com\">lifting.dakheera47.com</a>, tracking <strong>hundreds of sets</strong> over time with persistent analytics and performance trends.</p></li></ol>",
|
||||
"keywords": [],
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://lifting.dakheera47.com/"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"publications": {
|
||||
"name": "Publications",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "publications",
|
||||
"items": []
|
||||
},
|
||||
"references": {
|
||||
"name": "References",
|
||||
"columns": 1,
|
||||
"separateLinks": true,
|
||||
"visible": false,
|
||||
"id": "references",
|
||||
"items": [
|
||||
{
|
||||
"id": "f2sv5z0cce6ztjl87yuk8fak",
|
||||
"visible": true,
|
||||
"name": "Available upon request",
|
||||
"description": "",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"name": "Skills",
|
||||
"columns": 2,
|
||||
"separateLinks": true,
|
||||
"visible": true,
|
||||
"id": "skills",
|
||||
"items": [
|
||||
{
|
||||
"id": "jfgzfcwcg65k9gemuxlfe9m3",
|
||||
"visible": true,
|
||||
"name": "Frontend Development",
|
||||
"description": "",
|
||||
"level": 0,
|
||||
"keywords": [
|
||||
"React",
|
||||
"Next.js",
|
||||
"Tailwind CSS",
|
||||
"Strapi CMS",
|
||||
"Elementor",
|
||||
"GraphQL",
|
||||
"TypeScript",
|
||||
"CI/CD",
|
||||
"PWA Development",
|
||||
"AstroJS",
|
||||
"React Testing Library"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sk3957foopxir2hw4xzxqahh",
|
||||
"visible": true,
|
||||
"name": "Backend Development",
|
||||
"description": "",
|
||||
"level": 0,
|
||||
"keywords": [
|
||||
"Node.js",
|
||||
"Express.js",
|
||||
"MongoDB",
|
||||
"Supabase",
|
||||
"Firebase",
|
||||
"Docker",
|
||||
"FastAPI",
|
||||
"AWS S3",
|
||||
"AWS SES"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d9bddwdj6qreknhk644rm0bs",
|
||||
"visible": true,
|
||||
"name": "Leadership and Problem-Solving",
|
||||
"description": "",
|
||||
"level": 0,
|
||||
"keywords": [
|
||||
"Agile Project Management",
|
||||
"Conflict Resolution",
|
||||
"Creative Problem-Solving",
|
||||
"Decision-Making",
|
||||
"Effective Communication",
|
||||
"Adaptability"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gk4hrky0wnbsbdcmmud48zjh",
|
||||
"visible": true,
|
||||
"name": "Other Programming",
|
||||
"description": "",
|
||||
"level": 0,
|
||||
"keywords": [
|
||||
"Python Scripting",
|
||||
"PyAutoGUI",
|
||||
"Git",
|
||||
"GitHub",
|
||||
"Selenium",
|
||||
"Data Analysis",
|
||||
"Web Scraping",
|
||||
"Data Cleaning"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"metadata": {
|
||||
"template": "onyx",
|
||||
"layout": [
|
||||
[
|
||||
[
|
||||
"summary",
|
||||
"education",
|
||||
"experience",
|
||||
"projects",
|
||||
"references"
|
||||
],
|
||||
[
|
||||
"profiles",
|
||||
"skills",
|
||||
"certifications",
|
||||
"interests",
|
||||
"languages",
|
||||
"awards",
|
||||
"volunteer",
|
||||
"publications"
|
||||
]
|
||||
]
|
||||
],
|
||||
"css": {
|
||||
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||
"visible": false
|
||||
},
|
||||
"page": {
|
||||
"margin": 34,
|
||||
"format": "a4",
|
||||
"options": {
|
||||
"breakLine": false,
|
||||
"pageNumbers": false
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"background": "#ffffff",
|
||||
"text": "#000000",
|
||||
"primary": "#475569"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "IBM Plex Sans",
|
||||
"subset": "latin",
|
||||
"variants": [
|
||||
"regular"
|
||||
],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.75,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
137
resume-generator/generate_summary.py
Normal file
137
resume-generator/generate_summary.py
Normal file
@ -0,0 +1,137 @@
|
||||
"""
|
||||
Generate a tailored résumé summary using AI (OpenRouter API).
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
import pyperclip
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def load_profile(path: str = "./base.json") -> dict:
|
||||
"""Load the user's profile from a JSON file."""
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def load_job_description(from_clipboard: bool = True, path: str = None) -> str:
|
||||
"""
|
||||
Load the job description from clipboard or a file.
|
||||
|
||||
Args:
|
||||
from_clipboard: If True, read from system clipboard
|
||||
path: If from_clipboard is False, read from this file path
|
||||
|
||||
Returns:
|
||||
The job description text
|
||||
"""
|
||||
if from_clipboard:
|
||||
return pyperclip.paste().strip()
|
||||
if path:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return f.read().strip()
|
||||
raise ValueError("No job description source provided.")
|
||||
|
||||
|
||||
def _build_prompt(profile: dict, jd: str) -> str:
|
||||
"""Build the prompt for the AI model."""
|
||||
return f"""
|
||||
You are generating a tailored résumé summary for me.
|
||||
|
||||
Requirements:
|
||||
- Use keywords found in the job description.
|
||||
- Keep it concise but meaningful. Avoid fluff. Avoid long-winded text.
|
||||
- Include just enough detail to feel real and grounded.
|
||||
- Gently convey that I care about helping people and doing good work.
|
||||
- Do NOT invent experience or skills I don't have.
|
||||
- Maintain a warm, confident, human tone.
|
||||
- Target THIS specific job directly, so use ATS keywords, while remaining natural.
|
||||
- Use the profile to add context and details.
|
||||
|
||||
My profile (JSON fields merged):
|
||||
{json.dumps(profile, indent=2)}
|
||||
|
||||
Job description:
|
||||
{jd}
|
||||
|
||||
Write the résumé summary now.
|
||||
"""
|
||||
|
||||
|
||||
def _call_openrouter(prompt: str, model: str, api_key: str) -> str:
|
||||
"""Call OpenRouter API to generate text."""
|
||||
url = "https://openrouter.ai/api/v1/chat/completions"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"HTTP-Referer": "http://localhost",
|
||||
"X-Title": "ResumeSummaryScript",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=payload)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise RuntimeError(f"OpenRouter error {response.status_code}: {response.text}")
|
||||
|
||||
data = response.json()
|
||||
return data["choices"][0]["message"]["content"]
|
||||
|
||||
|
||||
def generate_resume_summary(
|
||||
profile_path: str = "./base.json",
|
||||
job_description: str = None,
|
||||
from_clipboard: bool = True,
|
||||
copy_to_clipboard: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Generate a tailored résumé summary using AI.
|
||||
|
||||
Uses the user's profile and a job description to generate a personalized
|
||||
summary section for a résumé, targeting the specific job.
|
||||
|
||||
Args:
|
||||
profile_path: Path to the profile JSON file
|
||||
job_description: Job description text (if None, uses from_clipboard/path)
|
||||
from_clipboard: If job_description is None, read JD from clipboard
|
||||
copy_to_clipboard: If True, copy the generated summary to clipboard
|
||||
|
||||
Returns:
|
||||
The generated résumé summary text
|
||||
"""
|
||||
load_dotenv()
|
||||
|
||||
api_key = os.getenv("OPENROUTER_API_KEY")
|
||||
model = os.getenv("MODEL", "openai/gpt-4o-mini")
|
||||
|
||||
if not api_key:
|
||||
raise RuntimeError("Missing OPENROUTER_API_KEY in .env")
|
||||
|
||||
profile = load_profile(profile_path)
|
||||
|
||||
if job_description is None:
|
||||
jd = load_job_description(from_clipboard=from_clipboard)
|
||||
else:
|
||||
jd = job_description
|
||||
|
||||
prompt = _build_prompt(profile, jd)
|
||||
summary = _call_openrouter(prompt, model, api_key)
|
||||
|
||||
if copy_to_clipboard:
|
||||
pyperclip.copy(summary)
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
summary = generate_resume_summary()
|
||||
|
||||
print("\n=== Generated Summary ===\n")
|
||||
print(summary)
|
||||
print("\n[Summary copied to clipboard]\n")
|
||||
105
resume-generator/rxresume_automation.py
Normal file
105
resume-generator/rxresume_automation.py
Normal file
@ -0,0 +1,105 @@
|
||||
"""
|
||||
Automate RXResume (rxresu.me) to import resume and export PDF using Playwright.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
# Configuration
|
||||
RXRESUME_EMAIL = os.getenv("RXRESUME_EMAIL", "ssarfaraz@lancashire.ac.uk")
|
||||
RXRESUME_PASSWORD = os.getenv("RXRESUME_PASSWORD", "thisisatestpassword")
|
||||
|
||||
BASE_DIR = Path(__file__).parent
|
||||
RESUME_JSON_PATH = BASE_DIR / "base.json"
|
||||
OUTPUT_DIR = BASE_DIR / "resumes"
|
||||
|
||||
|
||||
def login(page):
|
||||
"""Log in to RXResume."""
|
||||
page.goto("https://rxresu.me/auth/login")
|
||||
page.fill('input[placeholder="john.doe@example.com"]', RXRESUME_EMAIL)
|
||||
page.fill('input[type="password"]', RXRESUME_PASSWORD)
|
||||
page.click('button:has-text("Sign in")')
|
||||
page.wait_for_url("**/dashboard/resumes", timeout=15000)
|
||||
page.click('button:has-text("List")')
|
||||
|
||||
|
||||
def import_resume(page, json_path: Path):
|
||||
"""Import a resume JSON file."""
|
||||
page.click('h4:has-text("Import")')
|
||||
page.set_input_files('input[type="file"]', str(json_path))
|
||||
page.click('button:has-text("Validate")')
|
||||
page.click('button:has-text("Import")')
|
||||
|
||||
|
||||
def navigate_to_top_resume(page):
|
||||
"""Navigate to the first resume in the editor."""
|
||||
if "/dashboard/resumes" not in page.url:
|
||||
page.goto("https://rxresu.me/dashboard/resumes")
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# wait a beat for the list to update
|
||||
page.wait_for_timeout(1000)
|
||||
page.click('span[data-state="closed"]:first-of-type div:first-of-type')
|
||||
page.wait_for_url("**/builder/**", timeout=10000)
|
||||
|
||||
|
||||
def export_pdf(page, output_path: Path) -> Path:
|
||||
"""Export the resume as PDF."""
|
||||
page.wait_for_timeout(1500) # Wait for builder to fully load
|
||||
|
||||
selector = "div.inline-flex.items-center.justify-center.rounded-full.bg-background.px-4.shadow-xl button:last-of-type"
|
||||
|
||||
with page.expect_download(timeout=30000) as download_info:
|
||||
page.click(selector)
|
||||
|
||||
download = download_info.value
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
download.save_as(str(output_path))
|
||||
return output_path
|
||||
|
||||
|
||||
def generate_resume_pdf(
|
||||
output_filename: str = "resume.pdf",
|
||||
import_json: bool = False,
|
||||
json_path: Path = None,
|
||||
) -> Path:
|
||||
"""
|
||||
Import resume and export PDF.
|
||||
|
||||
Args:
|
||||
output_filename: Name of the output PDF file
|
||||
import_json: Whether to import a JSON file first
|
||||
json_path: Path to JSON file (if import_json is True)
|
||||
|
||||
Returns:
|
||||
Path to the generated PDF
|
||||
"""
|
||||
output_path = OUTPUT_DIR / output_filename
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
|
||||
try:
|
||||
login(page)
|
||||
|
||||
if import_json:
|
||||
import_resume(page, json_path or RESUME_JSON_PATH)
|
||||
|
||||
navigate_to_top_resume(page)
|
||||
export_pdf(page, output_path)
|
||||
finally:
|
||||
browser.close()
|
||||
|
||||
return output_path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pdf_path = generate_resume_pdf(
|
||||
output_filename="test_resume.pdf",
|
||||
import_json=True,
|
||||
)
|
||||
print(f"PDF saved: {pdf_path}")
|
||||
Loading…
x
Reference in New Issue
Block a user