diff --git a/20241220234456-github_notes.org b/20241220234456-github_notes.org index b536e32..f969729 100755 --- a/20241220234456-github_notes.org +++ b/20241220234456-github_notes.org @@ -6,7 +6,11 @@ * Cheat sheet +[[./assets/git-cheat-sheet-1.png]] +[[./assets/git-cheat-sheet-2.png]] + +[[./assets/Screenshot_20251227_153037.png]] * Open Source Projects: - [[https://github.com/codecrafters-io/build-your-own-x?tab=readme-ov-file][Build your own `x`]] diff --git a/20241231172511-book_notes.org b/20241231172511-book_notes.org index b75e6c4..96c695b 100755 --- a/20241231172511-book_notes.org +++ b/20241231172511-book_notes.org @@ -14,3 +14,5 @@ * [[id:dd55d635-59de-4ed9-8ff0-423782c2e0ae][clean-code]] * [[id:4426cc9a-1568-4fc8-9242-269654c43a3b][naqshe-hayat]] + +- [[id:dc9ece84-0b14-4afd-b25a-786cf1618996][Head First Design Patterns]] diff --git a/20250723200800-postgres.org b/20250723200800-postgres.org index e573f54..b726223 100755 --- a/20250723200800-postgres.org +++ b/20250723200800-postgres.org @@ -1,7 +1,7 @@ :PROPERTIES: :ID: 939e301b-6463-46a8-b57e-0af606e7e7ef :END: -#+title: postgres +#+title: Postgres #+filetags: :database:index: #+begin_src bash @@ -24,41 +24,41 @@ To get your phone logged in, there's two ways: Im thinking of having this as our shared calendar and file system. The only users are us 2 and its completely off the internet (self-hosted). -network issues resolved by: +** network issues resolved by: -docker network connect postgres_network postgres +#+begin_src bash + docker network connect postgres_network postgres -zaine@zaine-HP-ProDesk-400-G1-SFF [21:00:27] [~/docker-services/nextcloud] --> % psql -h zserver.zapto.org -p 5432 -U zaine -d zxq_db -Password for user zaine: -psql (14.18 (Ubuntu 14.18-0ubuntu0.22.04.1), server 17.5 (Debian 17.5-1.pgdg120+1)) -WARNING: psql major version 14, server major version 17. - Some psql features might not work. -Type "help" for help. + zaine@zaine-HP-ProDesk-400-G1-SFF [21:00:27] [~/docker-services/nextcloud] + -> % psql -h zserver.zapto.org -p 5432 -U zaine -d zxq_db + Password for user zaine: + psql (14.18 (Ubuntu 14.18-0ubuntu0.22.04.1), server 17.5 (Debian 17.5-1.pgdg120+1)) + WARNING: psql major version 14, server major version 17. + Some psql features might not work. + Type "help" for help. -zxq_db=# \c nextcloud -psql (14.18 (Ubuntu 14.18-0ubuntu0.22.04.1), server 17.5 (Debian 17.5-1.pgdg120+1)) -WARNING: psql major version 14, server major version 17. - Some psql features might not work. -You are now connected to database "nextcloud" as user "zaine". -nextcloud=# GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud; -GRANT -nextcloud=# GRANT USAGE ON SCHEMA public TO nextcloud; -GRANT CREATE ON SCHEMA public TO nextcloud; -GRANT -GRANT -nextcloud=# GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO nextcloud; -GRANT -nextcloud=# GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO nextcloud; -GRANT -nextcloud=# - -zaine -M22ZN-fmefj-RdF36-3BMfT-LfWo8 - -zaine -zxh123! + zxq_db=# \c nextcloud + psql (14.18 (Ubuntu 14.18-0ubuntu0.22.04.1), server 17.5 (Debian 17.5-1.pgdg120+1)) + WARNING: psql major version 14, server major version 17. + Some psql features might not work. + You are now connected to database "nextcloud" as user "zaine". + nextcloud=# GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud; + GRANT + nextcloud=# GRANT USAGE ON SCHEMA public TO nextcloud; + GRANT CREATE ON SCHEMA public TO nextcloud; + GRANT + GRANT + nextcloud=# GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO nextcloud; + GRANT + nextcloud=# GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO nextcloud; + GRANT + nextcloud=# + zaine + M22ZN-fmefj-RdF36-3BMfT-LfWo8 + zaine + zxh123! +#+end_src diff --git a/20250727122406-database_moc.org b/20250727122406-database_moc.org index 8d28807..e4bb877 100755 --- a/20250727122406-database_moc.org +++ b/20250727122406-database_moc.org @@ -1,7 +1,9 @@ :PROPERTIES: :ID: e448cd99-afee-4702-947f-644bb34dc1aa :END: -#+title: database_moc +#+title: Database MOC #+filetags: :database:moc: -[[id:939e301b-6463-46a8-b57e-0af606e7e7ef][postgres]] +- [[id:939e301b-6463-46a8-b57e-0af606e7e7ef][Postgres]] + +- [[id:c6cf8f7a-778f-4e83-b607-753ef8dbb3f1][Airflow]] diff --git a/20251229180039-airflow.org b/20251229180039-airflow.org new file mode 100644 index 0000000..b5080f0 --- /dev/null +++ b/20251229180039-airflow.org @@ -0,0 +1,25 @@ +:PROPERTIES: +:ID: c6cf8f7a-778f-4e83-b607-753ef8dbb3f1 +:END: +#+title: Airflow +#+filetags: :database:docs: + +* xcom + +XComs stands for cross-communications, and as the name suggests, it allows [[id:1ea42d73-3cd1-4cac-a6ee-028de21d08d5][Airflow Tasks]] to talk to each other (because by default, tasks are entirely isolated and can run on seperate machines). + +See [[https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/xcoms.html][this]] + +* parquets +*Key Takeaways* + +- What is Parquet: An open-source columnar storage file format for efficient analytics. +- Core Benefits: Superior compression, faster query performance (via column pruning and predicate pushdown), and schema evolution support. +- Common Alternatives: Compared to row-based (CSV, Avro), columnar (ORC), and table formats (Iceberg, Delta Lake). +- DuckDB & MotherDuck: Parquet integrates seamlessly with DuckDB for high-performance SQL queries directly on Parquet files. + +*What is Parquet?* + +Parquet is a columnar storage file format. When data engineers ask 'what is a Parquet file?', the simple answer is that it's a file that stores data in columns, not rows. This Parquet data format is designed for efficient data processing, particularly in the context of big data applications. Developed as part of the Apache Hadoop ecosystem, Parquet has gained widespread adoption due to its ability to optimize storage and query performance. + +[[https://motherduck.com/learn-more/why-choose-parquet-table-file-format/][See this]] diff --git a/20251229180039-airflow.org~ b/20251229180039-airflow.org~ new file mode 100644 index 0000000..ba6ba96 --- /dev/null +++ b/20251229180039-airflow.org~ @@ -0,0 +1,5 @@ +:PROPERTIES: +:ID: c6cf8f7a-778f-4e83-b607-753ef8dbb3f1 +:END: +#+title: airflow + diff --git a/20251229180156-airflow_tasks.org b/20251229180156-airflow_tasks.org new file mode 100644 index 0000000..d3b8c61 --- /dev/null +++ b/20251229180156-airflow_tasks.org @@ -0,0 +1,14 @@ +:PROPERTIES: +:ID: 1ea42d73-3cd1-4cac-a6ee-028de21d08d5 +:END: +#+title: Airflow Tasks +#+filetags: :airflow:database:definitions: + +A task in the context of airflow is the basic unit of execution. They are arranged into [[id:3b16e19a-62f0-4705-a451-a8790181941f][Dags]] and then have upstream and downstream dependencies set between them in order to express the order in which they should be run. + +There are three kinds of tasks: +1. Operators: they are predefined task templates that you can string together quickly. +2. Sensors: special subclass of operators, which are entirely about waiting for an external event to happen. +3. A Taskflow-decorated @task, which is a custom python function packaged as a task. + +[[https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/tasks.html][See this]] diff --git a/20251229180156-airflow_tasks.org~ b/20251229180156-airflow_tasks.org~ new file mode 100644 index 0000000..6246851 --- /dev/null +++ b/20251229180156-airflow_tasks.org~ @@ -0,0 +1,6 @@ +:PROPERTIES: +:ID: 1ea42d73-3cd1-4cac-a6ee-028de21d08d5 +:END: +#+title: Airflow Tasks +#+filetags: :airflow:database:docs: + diff --git a/20251229180249-airflow_dags.org b/20251229180249-airflow_dags.org new file mode 100644 index 0000000..abd2642 --- /dev/null +++ b/20251229180249-airflow_dags.org @@ -0,0 +1,7 @@ +:PROPERTIES: +:ID: 3b16e19a-62f0-4705-a451-a8790181941f +:END: +#+title: Airflow Dags +#+filetags: :airflow:database:definitions: + +A DAG (short for directed acyclic graphs) is a model that encapsulates everything needed to execute a workflow. [[https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html][See this]] diff --git a/20251229180249-airflow_dags.org~ b/20251229180249-airflow_dags.org~ new file mode 100644 index 0000000..a8a190d --- /dev/null +++ b/20251229180249-airflow_dags.org~ @@ -0,0 +1,6 @@ +:PROPERTIES: +:ID: 3b16e19a-62f0-4705-a451-a8790181941f +:END: +#+title: Airflow Dags + +A DAG (short for directed acyclic graphs) is a model that encapsulates everything needed to execute a workflow. [[https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html][See this]] diff --git a/all-files.org b/all-files.org index b1ba0cb..f1019d3 100755 --- a/all-files.org +++ b/all-files.org @@ -12,6 +12,9 @@ A complete alphabetical index of all published pages. - [[id:ed4c372b-0314-4b6e-9119-742f69b5e434][afp_lec_5]] - [[id:4bc71106-1d2b-4c71-836d-54b738fe5ff5][afp_week2]] - [[id:1f395b8c-cf55-43eb-9430-dd9449f6b575][afp_week5]] +- [[id:c6cf8f7a-778f-4e83-b607-753ef8dbb3f1][Airflow]] +- [[id:3b16e19a-62f0-4705-a451-a8790181941f][Airflow Dags]] +- [[id:1ea42d73-3cd1-4cac-a6ee-028de21d08d5][Airflow Tasks]] - [[id:e7f2302b-16eb-476d-a7b9-be12f077819d][AOC Notes]] * B - [[id:580cc3a5-af8e-4cbe-b5ad-5b06680e6c37][Backlog]] @@ -24,7 +27,7 @@ A complete alphabetical index of all published pages. - [[id:f9838952-9753-471b-a2cc-d72e01a53ef6][career_capital]] - [[id:dd55d635-59de-4ed9-8ff0-423782c2e0ae][clean-code]] * D -- [[id:e448cd99-afee-4702-947f-644bb34dc1aa][database_moc]] +- [[id:e448cd99-afee-4702-947f-644bb34dc1aa][Database MOC]] - [[id:d4f96bfb-b83d-449b-9f74-f602a1a3c2d3][deliberate_practice]] - [[id:631b2086-4b8f-4fe3-829d-be1dc014e293][Design patterns]] * E @@ -76,7 +79,7 @@ A complete alphabetical index of all published pages. - [[id:2c2d8df4-ad36-40ab-9f30-8a047b372956][old_nextcloud_server_code]] - [[id:a9829b5d-690d-4a21-ba94-ac8beac4d439][old_nginx_code]] * P -- [[id:939e301b-6463-46a8-b57e-0af606e7e7ef][postgres]] +- [[id:939e301b-6463-46a8-b57e-0af606e7e7ef][Postgres]] - [[id:2BFB84B2-2129-4AE3-8E69-290CA5BF9747][pre_work_prep_microlise]] - [[id:125c81dc-c14f-4b4d-93c6-0a2b157735ac][python-dictionary]] - [[id:9d534e89-7f0b-494c-bff6-7b3be05b85d1][python-lambda]] @@ -102,6 +105,7 @@ A complete alphabetical index of all published pages. * U - [[id:4a8edaed-9ebd-402b-9c5f-7a0cb4399102][uml_fyp]] - [[id:797d6e3e-98eb-4bc7-88b6-e096ef7306ad][uni_moc]] +- [[id:dc9ece84-0b14-4afd-b25a-786cf1618996][Untitled]] - [[id:d939e477-d1e9-43ea-960e-8727246d12a3][useful-c-imports]] * W - [[id:7612a9a6-ae70-4525-93a0-81bac857df39][wacom-notes]] diff --git a/assets/git-cheat-sheet-1.png b/assets/git-cheat-sheet-1.png new file mode 100644 index 0000000..14c91fb Binary files /dev/null and b/assets/git-cheat-sheet-1.png differ diff --git a/assets/git-cheat-sheet-2.png b/assets/git-cheat-sheet-2.png new file mode 100644 index 0000000..0abb241 Binary files /dev/null and b/assets/git-cheat-sheet-2.png differ diff --git a/assets/scripts/gallery-init.js b/assets/scripts/gallery-init.js index 548a989..34a83f4 100755 --- a/assets/scripts/gallery-init.js +++ b/assets/scripts/gallery-init.js @@ -4,79 +4,66 @@ document.addEventListener('DOMContentLoaded', () => { return; } - // 1) Wrap Org-exported images so they’re clickable - const imgs = document.querySelectorAll('.figure img, img.org-svg'); - imgs.forEach((img) => { - if (img.closest('a')) return; // already wrapped - const a = document.createElement('a'); - const href = img.currentSrc || img.src; - a.href = href; - a.dataset.img = href; // lets BP pre-size/raster slides - a.dataset.alt = img.alt || ''; - const setDims = () => { - a.dataset.width = img.naturalWidth || img.width || 1920; - a.dataset.height = img.naturalHeight || img.height || 1080; - }; - if (img.complete) setDims(); else img.addEventListener('load', setDims); - img.style.cursor = 'zoom-in'; - img.parentElement.insertBefore(a, img); - a.appendChild(img); - }); + function setupGalleries(root = document) { + const imgs = root.querySelectorAll('.figure img, img.org-svg'); + imgs.forEach((img) => { + if (img.closest('a')) return; + + const a = document.createElement('a'); + const href = img.currentSrc || img.src; + a.href = href; + a.dataset.img = href; + a.dataset.alt = img.alt || ''; + + const setDims = () => { + a.dataset.width = img.naturalWidth || img.width || 1920; + a.dataset.height = img.naturalHeight || img.height || 1080; + }; + if (img.complete) setDims(); else img.addEventListener('load', setDims); + + img.style.cursor = 'zoom-in'; + img.parentElement.insertBefore(a, img); + a.appendChild(img); + }); + + const containers = root.querySelectorAll('main, article, .content, body'); + containers.forEach((container) => { + const links = Array.from(container.querySelectorAll('.figure a, a:has(img.org-svg)')); + if (!links.length) return; + + links.forEach((link) => { + if (link.dataset.bpBound) return; + link.dataset.bpBound = 'true'; + + link.addEventListener('click', (e) => { + e.preventDefault(); + document.querySelectorAll(".theme-toggle").forEach(el => el.classList.add("hidden")); + + bp.open({ + items: links, + el: link, + caption: (el) => el.querySelector('img')?.alt || el.title || '', + maxZoom: 40, + onClose(containerEl) { + destroyPanZoom(); + teardownRotation(); + if (containerEl) containerEl.classList.add('bp-fadeout'); + document.querySelectorAll(".theme-toggle").forEach(el => el.classList.remove("hidden")); + }, + onOpen(containerEl) { setupRotation(containerEl); enhanceSVG(containerEl); }, + onUpdate(containerEl){ setupRotation(containerEl); enhanceSVG(containerEl); } + }); + }); + }); + }); + } - // 2) One global BP instance const bp = BiggerPicture({ target: document.body }); - // SVG pan/zoom handle - let activePanZoom = null; - const destroyPanZoom = () => { try { activePanZoom?.destroy(); } catch(_){} activePanZoom = null; }; - - // Simple rotate state (for non-SVG images) let activeContainer = null; let currentRotation = 0; let rotateControls = null; - - // 3) Build galleries per content container - const containers = document.querySelectorAll('main, article, .content, body'); - containers.forEach((container) => { - const links = Array.from(container.querySelectorAll('.figure a, a:has(img.org-svg)')); - if (!links.length) return; - - // Start the lightbox on click - links.forEach((link, index) => { - link.addEventListener('click', (e) => { - e.preventDefault(); - document.querySelectorAll(".theme-toggle").forEach(el => { - el.classList.add("hidden"); - }); - - bp.open({ - // IMPORTANT: pass the anchor ELEMENTS, not custom objects - items: links, - el: link, - caption: (el) => el.querySelector('img')?.alt || el.title || '', - maxZoom: 40, // for raster images (PNG/JPG); SVG handled separately - - // Fade-out polish + cleanup - onClose(containerEl) { - destroyPanZoom(); - teardownRotation(); - if (containerEl) containerEl.classList.add('bp-fadeout'); - const themeToggle = document.querySelector(".theme-toggle"); - if (themeToggle) { - themeToggle.classList.remove("hidden"); - } - }, - - // Called once after open and on every slide change - onOpen(containerEl) { setupRotation(containerEl); enhanceSVG(containerEl); }, - onUpdate(containerEl){ setupRotation(containerEl); enhanceSVG(containerEl); } - }); - }); - }); - }); - - // 4) Simple rotate buttons for raster images function ensureRotateControls() { if (rotateControls) return rotateControls; @@ -110,11 +97,11 @@ document.addEventListener('DOMContentLoaded', () => { return btn; }; - const left = mkBtn('⟲', 'Rotate image 90° left'); + const left = mkBtn('⟲', 'Rotate image 90° left'); const right = mkBtn('⟳', 'Rotate image 90° right'); left.addEventListener('click', (e) => { - e.stopPropagation(); // don’t close the lightbox + e.stopPropagation(); if (!activeContainer) return; currentRotation = (currentRotation - 90 + 360) % 360; applyRotation(); @@ -139,11 +126,10 @@ document.addEventListener('DOMContentLoaded', () => { const imgRoot = activeContainer.querySelector('.bp-img'); if (!imgRoot) return null; - let imgEl = imgRoot.querySelector('img'); + const imgEl = imgRoot.querySelector('img'); if (!imgEl) return null; const src = (imgEl.currentSrc || imgEl.src || '').toLowerCase(); - // We only rotate raster images; SVGs are handled via svg-pan-zoom if (src.endsWith('.svg')) return null; let wrapper = imgRoot.querySelector('.bp-rotate-wrapper'); @@ -156,7 +142,6 @@ document.addEventListener('DOMContentLoaded', () => { imgRoot.appendChild(wrapper); wrapper.appendChild(imgEl); } else if (!wrapper.contains(imgEl)) { - // Slide changed and BiggerPicture replaced the wrapper.innerHTML = ''; wrapper.appendChild(imgEl); } @@ -181,13 +166,12 @@ document.addEventListener('DOMContentLoaded', () => { function teardownRotation() { activeContainer = null; currentRotation = 0; - if (rotateControls) { - rotateControls.style.display = 'none'; - } + if (rotateControls) rotateControls.style.display = 'none'; } - - // 4) If current slide is an SVG, swap to inline + enable svg-pan-zoom + let activePanZoom = null; + const destroyPanZoom = () => { try { activePanZoom?.destroy(); } catch(_){} activePanZoom = null; }; + async function enhanceSVG(containerEl) { try { destroyPanZoom(); @@ -199,14 +183,12 @@ document.addEventListener('DOMContentLoaded', () => { const isSVG = src.toLowerCase().endsWith('.svg'); const htmlLayer = containerEl.querySelector('.bp-html'); if (!isSVG || !htmlLayer) { - // ensure any previous holder is removed and bitmap is visible const old = htmlLayer?.querySelector('.bp-svg-holder'); if (old) old.remove(); imgEl.style.visibility = ''; return; } - // Create/clear holder let holder = htmlLayer.querySelector('.bp-svg-holder'); if (!holder) { holder = document.createElement('div'); @@ -217,10 +199,8 @@ document.addEventListener('DOMContentLoaded', () => { } holder.innerHTML = ''; - // Hide the bitmap so only the inline SVG shows imgEl.style.visibility = 'hidden'; - // Inline the SVG const res = await fetch(src, { cache: 'force-cache' }); const text = await res.text(); holder.innerHTML = text; @@ -228,9 +208,9 @@ document.addEventListener('DOMContentLoaded', () => { const svg = holder.querySelector('svg'); if (!svg) { imgEl.style.visibility = ''; return; } - svg.style.maxWidth = '95vw'; + svg.style.maxWidth = '95vw'; svg.style.maxHeight = '95vh'; - svg.style.display = 'block'; + svg.style.display = 'block'; if (typeof window.svgPanZoom === 'function') { activePanZoom = svgPanZoom(svg, { @@ -239,11 +219,10 @@ document.addEventListener('DOMContentLoaded', () => { fit: true, center: true, minZoom: 0.05, - maxZoom: 400, // effectively "unlimited" + maxZoom: 400, zoomScaleSensitivity: 0.25, dblClickZoomEnabled: true }); - // Keep wheel inside lightbox holder.addEventListener('wheel', (e) => e.stopPropagation(), { passive: true }); } else { console.warn('[gallery-init] svg-pan-zoom not loaded'); @@ -252,4 +231,27 @@ document.addEventListener('DOMContentLoaded', () => { console.error('[gallery-init] SVG enhance failed:', err); } } + + setupGalleries(document); + + const dynamicObserver = new MutationObserver((mutations) => { + for (const m of mutations) { + for (const node of m.addedNodes) { + if (!(node instanceof HTMLElement)) continue; + if (node.querySelectorAll) setupGalleries(node); + } + } + }); + dynamicObserver.observe(document.body, { childList: true, subtree: true }); + + let lastUrl = location.href; + const urlObserver = new MutationObserver(() => { + const currentUrl = location.href; + if (currentUrl !== lastUrl) { + lastUrl = currentUrl; + console.log('[gallery-init] URL changed → reinitializing galleries'); + setupGalleries(document); + } + }); + urlObserver.observe(document, { subtree: true, childList: true }); }); diff --git a/book-notes/Head First Design Patterns.org b/book-notes/Head First Design Patterns.org new file mode 100644 index 0000000..b603277 --- /dev/null +++ b/book-notes/Head First Design Patterns.org @@ -0,0 +1,57 @@ +* Head First Design Patterns +:PROPERTIES: +:NOTER_DOCUMENT: ../../../pdfs/bootcamp/Head First Design Patterns.pdf +:ID: dc9ece84-0b14-4afd-b25a-786cf1618996 +:END: +** Chapter 1 +:PROPERTIES: +:NOTER_PAGE: 36 +:END: +*** Structural: +The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it. + +- Encapsulate what varies + +- Favor composition over inheritance + +- Program to interfaces, not implementations + +** Chapter 2 +:PROPERTIES: +:NOTER_PAGE: 82 +:END: +*** Observer: +The Observer Pattern defines a one-to-many dependency between objects so that when +one object changes state, all of its dependents are notified and updated automatically. + +Subjects, or as we also know them, Observables, update Observers using a common +interface + +Observers are loosely coupled in that the Observable knows nothing about them, +other than that they implement the Observer interface. + +You can push or pull data from the Observable when using the pattern (pull is +considered more “correct”). + +Don’t depend on a specific order of notification for your Observers. + +Java has several implementations of the Observer Pattern, including the general +purpose java.util.Observable + +- Strive for loosely coupled designs between objects that interact +** Notes for page 137 +:PROPERTIES: +:NOTER_PAGE: 137 +:END: + +- Classes should be open for extension, but closed for modification. +** Notes for page 142 +:PROPERTIES: +:NOTER_PAGE: 142 +:END: +Notes for the decaroter pattern. +** Notes for page 143 +:PROPERTIES: +:NOTER_PAGE: 143 +:END: +Decorator pattern: The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. diff --git a/book-notes/Head First Design Patterns.org~ b/book-notes/Head First Design Patterns.org~ new file mode 100644 index 0000000..abe9704 --- /dev/null +++ b/book-notes/Head First Design Patterns.org~ @@ -0,0 +1,5 @@ +* Head First Design Patterns +:PROPERTIES: +:NOTER_DOCUMENT: ../../../pdfs/bootcamp/Head First Design Patterns.pdf +:ID: dc9ece84-0b14-4afd-b25a-786cf1618996 +:END: