added makefile and cleaned backups
This commit is contained in:
20
backups/02-11-week-review.org~
Normal file
20
backups/02-11-week-review.org~
Normal file
@@ -0,0 +1,20 @@
|
||||
#+TITLE: [02-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-02 Sun>
|
||||
#+filetags: :review:
|
||||
|
||||
[Was a mid week file, then moved it to a review file]
|
||||
|
||||
* Mid Week Review
|
||||
- Mostly work related updates. Working from home on monday 10th (for trial, so we get used to it and connecting to the VPN)
|
||||
- Subsequently we will be able to work from home on mondays and fridays for the following weeks (beginning either 17th or 24th?)
|
||||
- Still working on powerbi (not a fan)
|
||||
- Upskilling needs to take more this week and the next
|
||||
- Probation meeting on thursday next week [06/11]
|
||||
- It's getting darker earlier and more windy
|
||||
|
||||
* TODOS
|
||||
** DONE
|
||||
Write the retrospective notes
|
||||
** DONE
|
||||
More competencies
|
||||
8
backups/04-01-week-review.org~
Normal file
8
backups/04-01-week-review.org~
Normal file
@@ -0,0 +1,8 @@
|
||||
#+TITLE: [04-01-2026] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2026-01-04 Sun 12:12>
|
||||
#+filetags: :review:
|
||||
#+WIP: t
|
||||
#+COMMENTS: t
|
||||
#+SLUG: 04-01-26-week-review
|
||||
|
||||
64
backups/07-12-week-review.org~
Normal file
64
backups/07-12-week-review.org~
Normal file
@@ -0,0 +1,64 @@
|
||||
#+TITLE: [07-12-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-12-07 Sun 20:34>
|
||||
#+filetags: :review:
|
||||
#+WIP:
|
||||
|
||||
* Previous Week List:
|
||||
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
|
||||
|
||||
* Weekly Review:
|
||||
This week has been so busy with getting the airflow working and the datamarts tested. Our team suggested to use the Friday as a 'training day', I told myself I'd work until lunch, then use the afternoon to tick off some competencies and reading, however it took me all day to finish off the DAG's.
|
||||
|
||||
- <2025-12-01 Mon>:
|
||||
- I continued working on the Datamart testing, getting majority of the tables tested
|
||||
- <2025-12-02 Tue>
|
||||
- Started to work on the airflow testing
|
||||
- Had our retros, demos and refinement sessions
|
||||
- <2025-12-03 Wed>
|
||||
- Created and finished off the suite testing for the airflows. Started to look into pipelines
|
||||
- <2025-12-04 Thu>
|
||||
- Fixing some airflow tests
|
||||
- Started to get the STR (System Team Repo) merged into a datamart/airflow
|
||||
- Made a pipeline with the help of Oliver Gamble (main and gated)
|
||||
- <2025-12-05 Fri>
|
||||
- Completed the airflow DAG for repositories
|
||||
|
||||
** Daily retrospectives:
|
||||
|
||||
[[../../../assets/images/reviews/07-12-25-daily-retros.jpg]]
|
||||
|
||||
** Weekly retrospectives:
|
||||
|
||||
[[../../../assets/sketchnotes/07-12-2025-retrospectives.svg]]
|
||||
|
||||
* Other
|
||||
|
||||
- In the Weekly List, I need to document things outside of competencies. The weekly review should not be limited to work.
|
||||
- I added a new docker service for running a self hosted VPN (~wg-easy~). The link for this is [[https:wgeasy.zainezq.com][here]].
|
||||
- I used to use a reading tracker to see which books I'm reading and have read. I want to get this back in use. An entry looks like:
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
,* Book Title
|
||||
:PROPERTIES:
|
||||
:Author:
|
||||
:Status:
|
||||
:Group:
|
||||
:STARTED:
|
||||
:FINISHED:
|
||||
:Category:
|
||||
:END:
|
||||
|
||||
#+END_SRC
|
||||
|
||||
- The current amount of time it takes to build this website can be found using the following command: ~time emacs -Q --script build-site.el~. And currently it outputs:
|
||||
|
||||
#+BEGIN_SRC zsh
|
||||
real 0m8.468s
|
||||
user 0m7.316s
|
||||
sys 0m1.068s
|
||||
#+END_SRC
|
||||
The old website used to take a full minute to build, so this isn't too bad (also considering the other website was much /smaller/ in comparison to this one).
|
||||
42
backups/09-11-week-review.org~
Normal file
42
backups/09-11-week-review.org~
Normal file
@@ -0,0 +1,42 @@
|
||||
#+TITLE: [09-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE:<2025-11-09 Sun 20:08>
|
||||
#+filetags: :review:
|
||||
|
||||
* Weekly review
|
||||
|
||||
- Week 5 work finished!
|
||||
- Working from home on monday 10th.
|
||||
- Maybe do weekly work retrospectives? Using the start stop continue model.
|
||||
- Probation meeting went quite well Alhamdulillah. Keep doing what you're doing was the message.
|
||||
- Should start to record successes, either here or on post-it notes. Helps with imposter syndrome.
|
||||
- Led a meeting for the first time (07/11). Was with Lloyd and Oli from team monopoly (SecDevOps). They understood the problem statement clearly and everything was explained properly to them.
|
||||
- Bowling Kata was reduced to 29 minutes.
|
||||
|
||||
Some thoughts about the nginx reverse proxying architecture. This week I transferred all the docker services to a subdomain.
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-nginx-architecture.svg]]
|
||||
|
||||
Weekly retrospective using the start stop continue model:
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-retrospectives.svg]]
|
||||
|
||||
Here is the current state of the competencies (20.5%):
|
||||
|
||||
[[../../../assets/images/competencies/competencies-09-11-25.png]]
|
||||
|
||||
During the meeting, we were told to achieve 60% by the end of January.
|
||||
|
||||
And here is the dev plan:
|
||||
|
||||
[[../../../assets/images/competencies/dev-plan-09-11-25.png]]
|
||||
|
||||
|
||||
* Next week:
|
||||
|
||||
- Competencies to do:
|
||||
- Aware of the Benefits of Normalisation
|
||||
- Aware of PowerShell, Python, Bash and Pester
|
||||
- Aware of Lean, Agile & extreme programming Principles
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
21
backups/14-12-week-review.org~
Normal file
21
backups/14-12-week-review.org~
Normal file
@@ -0,0 +1,21 @@
|
||||
#+TITLE: [14-12-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-12-09 Tue 12:12>
|
||||
#+filetags: :review:
|
||||
#+WIP:
|
||||
|
||||
|
||||
* Week List:
|
||||
- +Can Measure Computer Performance on Disk, CPU, RAM, Network+
|
||||
- +Tests SQL queries and tSQL code+
|
||||
- +Aware of Basic Infrastructure as Code Concepts+
|
||||
- +Aware of Distributed Systems+
|
||||
- +Can Measure Computer Performance on Disk, CPU, RAM, Network+
|
||||
- +Tests SQL queries and tSQL code+
|
||||
- +Aware of Archiving Procedures+
|
||||
|
||||
* Weekly Review:
|
||||
|
||||
Not much to show this week, I've been really busy trying to finalise the project, although I reckon it may take another week. I did get to do a lot more competencies at the start of the week however.
|
||||
|
||||
* Other
|
||||
87
backups/16-11-week-review.org~
Normal file
87
backups/16-11-week-review.org~
Normal file
@@ -0,0 +1,87 @@
|
||||
#+TITLE: [16-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE:<2025-11-10 Mon 17:44>
|
||||
#+filetags: :review:
|
||||
#+WIP:
|
||||
|
||||
* Previous Week List:
|
||||
|
||||
- Competencies to do:
|
||||
- +Aware of the Benefits of Normalisation+
|
||||
- Aware of PowerShell, Python, Bash and Pester
|
||||
- +Aware of Lean, Agile & extreme programming Principles+
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
- +Understands responsive design+
|
||||
- +Understands Network design+
|
||||
|
||||
* Weekly review:
|
||||
|
||||
- <2025-11-10 Mon>:
|
||||
- Got assigned to create a database that can help the whole company. Value added work! The database will have system names linked to the team names. In other words, which teams are working under which systems? Also it will have the repo names and which teams own which repos.
|
||||
- <2025-11-11 Tue>
|
||||
- Worked on getting the DB for the system-team-repo. Met with Jez and he said to get the IPT names and use them instead. [reminder to attatch the links for this].
|
||||
- Created a powershell script to load all the data into the SQL server (local)
|
||||
- <2025-11-12 Wed>
|
||||
- Met with the whole team monopoly (+ others). -> First time being in a group huddle
|
||||
- Was interesting to see how [[https://www.yworks.com/][yworks]] is used
|
||||
- <2025-11-13 Thu>
|
||||
- Angular training. Was taught about the new Angular 19 changes and how to use them. Things like the the ~@~ decorator was discussed.
|
||||
- <2025-11-14 Fri>
|
||||
- Training on datamarts and airflow. Learnt a lot about how they can be used. I wrote a few slack messages explaining to the team what it is, and also got clarification from Filipo. Also sent a request to create a new repository for this project.
|
||||
|
||||
Was able to complete 4/5 competencies this week. The reason for not completing them is that Friday was used mainly for meetings and other admin tasks. In hindsight, I /could/ have completed them, as there were times when I chose to not do value added work.
|
||||
|
||||
[[../../../assets/sketchnotes/16-11-2025-retrospectives.svg]]
|
||||
|
||||
Daily retrospectives:
|
||||
|
||||
[[../../../assets/images/reviews/16-11-25-daily-retros.jpg]]
|
||||
|
||||
One thing that really helped this week is ~guac~. I was able to use this whilst at work, very handy when wanting to refer to some notes/files that is only located at home.
|
||||
|
||||
[[../../../assets/images/reviews/15-11-2025-guac.png]]
|
||||
|
||||
* Other
|
||||
** Airflow/Datamart
|
||||
[[../../../posts/career/airflow.org][Airflow Post]]
|
||||
|
||||
*** Diagram
|
||||
*A work colleague has written the following and made this diagram*
|
||||
|
||||
[[../../../assets/images/diagrams/airflow.png]]
|
||||
|
||||
1. AZDO repo for our datamart to go in. Clone the data mart example template
|
||||
2. Datamart, can use one big one that has different tables for everything that we need.
|
||||
3. Local airflow that calls api and gets data. continuation tokens can be used. there is a good example
|
||||
4. Upload everything to microlise servers where it is run automatically
|
||||
5. Power bi, change data sources to be references to our sql. Can have all tables in power bi. Use filters
|
||||
|
||||
*** Summary that I wrote in slack:
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DataMart.SQLServer: very useful link explaining what datamarts are, ELT (extract, load, transform) orchestration and how to set this up
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DataMart.DataEngineering.InsightReadiness: an example of what a datamart repo looks like. in the template repo, the two important files are tsqlt and DataMartTemplate (in this instance its called InsightReadiness )
|
||||
|
||||
- tsqlt coverage should be above 60%
|
||||
|
||||
- https://microliseuk.sharepoint.com/sites/msteams_bb7636: This link is the CoP (community of practice) for the airflow. You'll find useful information regarding what apache airflow is and how to set things up.
|
||||
|
||||
- https://microliseuk.sharepoint.com/sites/msteams_bb7636/SitePages/Getting-started-with-Airflow.aspx: This link tells you how to set airflow up locally. Once you're done, this will be published to an airflow scheduler platform. Keep in mind that when you want to publish this, document that it succeeds in the PR.
|
||||
|
||||
- Just a summary from Filippo:
|
||||
- A DataMart it's simply a database that is made ad-hoc to contain a certain subset of data (Specific tables with specific columns).
|
||||
- Once that is created Airflow is used to put data in that database. So it extract data from other sources (APIS, databases) and loads it into the datamart we have created.
|
||||
- Once you get Airflow locally working and a new Datamart (deployed locally) you can then write a dag to load data into the Mart.
|
||||
|
||||
- http://airmastqa01.mms.local:8080/home: this link shows all the DAGs that are currently running in a testing environment, and when they are scheduled. Login using MSUP (edited)
|
||||
|
||||
- http://airmastprd01.mms.local:8080/home: This link is the prod version
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DevDocs?path=/Airflow/HowTo/PowerBiReportRefresh.md&version=GBmain&_a=preview: Might prove to be useful when we do the powerbi refresh thing...
|
||||
|
||||
* Next week:
|
||||
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
- Aware of PowerShell, Python, Bash and Pester
|
||||
76
backups/23-11-week-review.org~
Normal file
76
backups/23-11-week-review.org~
Normal file
@@ -0,0 +1,76 @@
|
||||
#+TITLE: [23-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-17 Mon 18:05>
|
||||
#+filetags: :review:
|
||||
#+WIP:
|
||||
|
||||
* Previous Week List:
|
||||
|
||||
- Competencies to do:
|
||||
- +Aware of PowerShell, Python, Bash and Pester+
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
|
||||
There wasn't much time to do competencies this week because the new sprint started. We were told that this project should ideally be finished within the next two weeks so that we can start working on some other things (Angular related; Kevin did say that it would be related to creating a system that shows who makes the commits, how often and the relationship between committers).
|
||||
|
||||
* Weekly Review
|
||||
- <2025-11-17 Mon>:
|
||||
- Created a new repository for the team using the #help-azdo channel.
|
||||
- Set up this repo by renaming references etc.
|
||||
- Played around with datamarts, got the team and system tables working, although haven't yet written the ~tSQLt~ tests for them yet.
|
||||
- <2025-11-18 Tue>
|
||||
- Demo-ed the STR (system team repo) database.
|
||||
- Started to create the tables for the Mean time to restore and lead time for change.
|
||||
- <2025-11-19 Wed>
|
||||
- Finished off creating the data tables for MTTR and LTTC
|
||||
- <2025-11-20 Thu>
|
||||
- Started creating Airflows for the repositories table
|
||||
- <2025-11-21 Fri>
|
||||
- Data science training, Research Operations, Kafka, Airflow
|
||||
** Daily retrospectives:
|
||||
|
||||
[[../../../assets/images/reviews/23-11-25-daily-retros.jpg]]
|
||||
|
||||
** Weekly retrospectives
|
||||
|
||||
[[../../../assets/sketchnotes/23-11-2025-retrospectives.svg]]
|
||||
|
||||
* Other
|
||||
|
||||
** Books
|
||||
|
||||
As I finished another book and also spent some time adding more services, I wanted to collate a list of books (of a wide genre) to read. One I came across is [[https://theinvisiblementor.com/ikigai-the-japanese-secret-to-a-long-and-happy-life-summary/][Ikigai]].
|
||||
|
||||
** reader.zainezq.com
|
||||
Something interesting I found when I wanted to take notes on the book called /The Toyota Way/; this is a service that lets you read books as well as convert a ~pdf~ file to ~epub~.
|
||||
|
||||
[[../../../assets/images/reviews/23-11-2025-reader.png]]
|
||||
|
||||
** booksvnc.zainezq.com
|
||||
In order to add books, edit metadata and start the server for the ~reader.zainezq.com~
|
||||
|
||||
[[../../../assets/images/reviews/23-11-2025-vnc.png]]
|
||||
|
||||
** netdata.zainezq.com
|
||||
|
||||
One thing I wanted to have information for is:
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
|
||||
uname -a
|
||||
hostnamectl
|
||||
cat /etc/os-release
|
||||
lscpu
|
||||
free -h
|
||||
lsblk -f
|
||||
df -h
|
||||
ip a
|
||||
|
||||
#+END_SRC
|
||||
|
||||
[[../../../assets/images/reviews/23-11-2025-netdata.png]]
|
||||
|
||||
* Next Week
|
||||
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
0
backups/28-12-week-review.org~
Normal file
0
backups/28-12-week-review.org~
Normal file
165
backups/30-11-week-review.org~
Normal file
165
backups/30-11-week-review.org~
Normal file
@@ -0,0 +1,165 @@
|
||||
#+TITLE: [30-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-30 Sun 17:09>
|
||||
#+filetags: :review:
|
||||
#+WIP:
|
||||
|
||||
* Previous Week List:
|
||||
|
||||
- Competencies to do:
|
||||
- +Aware of relational databases+
|
||||
- +Aware of TLS and SSL+
|
||||
- +Aware of good error handling and logging+
|
||||
- Understanding of TCP/IP
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
|
||||
Been extremely busy with this sprint, getting the work delivered in a timely manner; there wasn't as much time to focus on competencies.
|
||||
|
||||
* Weekly Review
|
||||
- <2025-11-24 Mon>:
|
||||
- Got the news the company is reducing staff. See this [[https://www.thebusinessdesk.com/eastmidlands/news/2104597-microlise-issues-profit-warning-after-weaker-than-expected-trading][article]]
|
||||
- Got the DAG for PR's to work
|
||||
- <2025-11-25 Tue>
|
||||
- Worked mainly on threads and commits DAGs
|
||||
- <2025-11-26 Wed>
|
||||
- We had a meeting with Kevin on the scoring system for the redundancy thing
|
||||
- Finished creating and testing all the DAG's
|
||||
- <2025-11-27 Thu>
|
||||
- Refinement meeting
|
||||
- Started creating base case tests for SQL
|
||||
- Made a script to automate the creation of testing files
|
||||
- <2025-11-28 Fri>
|
||||
- Finished base case tests
|
||||
- Training with Lloyd on SRE and Techops
|
||||
** Daily retrospectives:
|
||||
|
||||
[[../../../assets/images/reviews/30-11-25-daily-retros.jpg]]
|
||||
|
||||
** Weekly retrospectives
|
||||
|
||||
[[../../../assets/sketchnotes/30-11-2025-retrospectives.svg]]
|
||||
|
||||
* Other
|
||||
|
||||
** Powershell script:
|
||||
|
||||
Here is the powershell script that generates the files for testing:
|
||||
|
||||
#+BEGIN_SRC ps1
|
||||
|
||||
# Define the base folders
|
||||
$securityFolder = "C:\Users\zqayyum\Source\Repos\DataMart.Academy.DoraMetrics\tsqlt\Security"
|
||||
$scriptsFolder = "C:\Users\zqayyum\Source\Repos\DataMart.Academy.DoraMetrics\tsqlt"
|
||||
|
||||
|
||||
# Create base folders if they don't exist
|
||||
if (!(Test-Path $securityFolder)) { New-Item -ItemType Directory -Path $securityFolder }
|
||||
if (!(Test-Path $scriptsFolder)) { New-Item -ItemType Directory -Path $scriptsFolder }
|
||||
|
||||
# Define main file names (schemas)
|
||||
$fileNames = @(
|
||||
"testMergeStagingDataCommits.sql",
|
||||
"testMergeStagingDataEnvironments.sql",
|
||||
"testMergeStagingDataPullRequests.sql",
|
||||
"testMergeStagingDataReleases.sql",
|
||||
"testMergeStagingDataRepositories.sql",
|
||||
"testMergeStagingDataThreads.sql"
|
||||
)
|
||||
|
||||
# Define stored procedure test files
|
||||
$storedProcFiles = @(
|
||||
"Test changed rows are logged.sql",
|
||||
"Test changed rows are not logged as inserted.sql",
|
||||
"Test changed rows are updated.sql",
|
||||
"Test errors are logged.sql",
|
||||
"Test import date on changed rows is updated.sql",
|
||||
"Test new rows are inserted.sql",
|
||||
"Test new rows are not logged as updated.sql"
|
||||
)
|
||||
|
||||
foreach ($name in $fileNames) {
|
||||
# Create .sql file under Security
|
||||
$filePath = Join-Path $securityFolder $name
|
||||
$schemaName = [System.IO.Path]::GetFileNameWithoutExtension($name)
|
||||
|
||||
# Content for schema file
|
||||
$schemaContent = @"
|
||||
CREATE SCHEMA [$schemaName]
|
||||
AUTHORIZATION [dbo];
|
||||
|
||||
GO
|
||||
EXECUTE sp_addextendedproperty
|
||||
@name = N'tSQLt.TestClass',
|
||||
@value = 1,
|
||||
@level0type = N'SCHEMA',
|
||||
@level0name = N'$schemaName';
|
||||
GO
|
||||
"@
|
||||
|
||||
Set-Content -Path $filePath -Value $schemaContent
|
||||
|
||||
# Create schema folder under Scripts
|
||||
$schemaFolderPath = Join-Path $scriptsFolder $schemaName
|
||||
if (!(Test-Path $schemaFolderPath)) {
|
||||
New-Item -ItemType Directory -Path $schemaFolderPath | Out-Null
|
||||
}
|
||||
|
||||
# Create Stored Procedures subfolder
|
||||
$storedProcFolderPath = Join-Path $schemaFolderPath "Stored Procedures"
|
||||
if (!(Test-Path $storedProcFolderPath)) {
|
||||
New-Item -ItemType Directory -Path $storedProcFolderPath | Out-Null
|
||||
}
|
||||
|
||||
# Create stored procedure files with dynamic content
|
||||
foreach ($procFile in $storedProcFiles) {
|
||||
$procFilePath = Join-Path $storedProcFolderPath $procFile
|
||||
$procName = [System.IO.Path]::GetFileNameWithoutExtension($procFile)
|
||||
|
||||
$procContent = @"
|
||||
CREATE PROCEDURE $schemaName.[$procName]
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- Assemble
|
||||
DECLARE @RowsUpdated INT;
|
||||
|
||||
EXEC tsqlt.FakeTable @SchemaName = 'staging', @TableName = 'tbl_TemplateTable';
|
||||
EXEC tsqlt.FakeTable @SchemaName = 'data', @TableName = 'tbl_TemplateTable';
|
||||
EXEC tsqlt.FakeTable @SchemaName = 'logging', @TableName = 'tbl_ExecutionCounts';
|
||||
|
||||
INSERT INTO data.tbl_TemplateTable(UnitDescription, TrunionDisplacementInMM)
|
||||
VALUES('UT7000', '9');
|
||||
|
||||
INSERT INTO staging.tbl_TemplateTable(UnitDescription, TrunionDisplacementInMM)
|
||||
VALUES('UT7000', '6');
|
||||
|
||||
-- Act
|
||||
EXEC staging.pr_MergeStagingData;
|
||||
|
||||
-- Assert
|
||||
SELECT @RowsUpdated = RowsUpdated FROM logging.tbl_ExecutionCounts;
|
||||
|
||||
EXEC tsqlt.AssertEquals
|
||||
@Actual = @RowsUpdated,
|
||||
@Expected = 1;
|
||||
END
|
||||
"@
|
||||
|
||||
Set-Content -Path $procFilePath -Value $procContent
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "All files and folders created successfully with dynamic procedure content"
|
||||
|
||||
#+END_SRC
|
||||
|
||||
** Finances
|
||||
I decided to spin up a docker container to manage finances. For the moment there is no way to connect to a Bank without using an intermediary (privacy concerns).
|
||||
|
||||
[[../../../assets/images/reviews/30-11-2025-acutal.png]]
|
||||
|
||||
* Next Week
|
||||
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
23
backups/Makefile~
Normal file
23
backups/Makefile~
Normal file
@@ -0,0 +1,23 @@
|
||||
.PHONY: all clean norm help
|
||||
|
||||
# Default target: full build with search index
|
||||
all:
|
||||
@echo "Building project (full rebuild with search index)..."
|
||||
emacs -Q --script build.el
|
||||
|
||||
# Clean output directory
|
||||
clean:
|
||||
@echo "Cleaning output directory..."
|
||||
rm -rf output/
|
||||
|
||||
norm:
|
||||
@echo "sorting out the backups..."
|
||||
find . -path ./backups -prune -o -type f -name '*~' -exec mv {} backups/ \;
|
||||
|
||||
# Show help message
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " make - Full rebuild"
|
||||
@echo " make clean - Remove output directory"
|
||||
@echo " make norm - Move all files that end with ~ to the backup folder"
|
||||
@echo " make help - Show this help message"
|
||||
63
backups/airflow.org~
Normal file
63
backups/airflow.org~
Normal file
@@ -0,0 +1,63 @@
|
||||
#+TITLE: Datamarts, Airflow and DAG's
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE:<2025-11-15 Sat 18:37>
|
||||
#+filetags: :learning:notes:
|
||||
|
||||
* What they are:
|
||||
Summary from Filippo:
|
||||
- A DataMart it's simply a database that is made ad-hoc to contain a certain subset of data (Specific tables with specific columns).
|
||||
- Once that is created Airflow is used to put data in that database. So it extract data from other sources (APIS, databases) and loads it into the datamart we have created.
|
||||
- Once you get Airflow locally working and a new Datamart (deployed locally) you can then write a dag to load data into the Mart.
|
||||
|
||||
A data warehouse is where *all* the data is stored (sales, marketing, HR etc.). A data mart is just what one group needs, so for example, a finance data mart would contain invoices, payments and budgets data.
|
||||
|
||||
** Apache Airflow
|
||||
It is a tool that lets you automate and schedule data tasks. It:
|
||||
- Runs jobs on a schedule (e.g. every night at 1am).
|
||||
- Knows what needs to run before what.
|
||||
- Shows you a nice UI so you can see: what succeeded, what failed and where it’s stuck.
|
||||
|
||||
So: Airflow = a smart scheduler and organiser for data workflows.
|
||||
|
||||
** DAG
|
||||
DAG stands for directed acyclic graph. In simple terms, it is just a workflow made of tasks in a certain order. For example, a DAG can be: ~get_data~, then ~clean_data~, then ~load_data_into_datamart~, and finally ~send_email~. DAG is the whole workflow, the tasks are the individual steps inside the DAG.
|
||||
|
||||
*** ELT (Extract → Load → Transform)
|
||||
|
||||
Do the cleaning after loading into the warehouse. Flow:
|
||||
- Extract from source systems
|
||||
- Load raw data directly into the warehouse
|
||||
- Transform inside the warehouse (usually with SQL)
|
||||
|
||||
Here is a diagram that explains this process:
|
||||
|
||||
[[../../assets/sketchnotes/elt-yed.svg]]
|
||||
|
||||
** Diagram
|
||||
*Sam has written the following and made this diagram*
|
||||
|
||||
[[../../../assets/images/diagrams/airflow.png]]
|
||||
|
||||
1. AZDO repo for our datamart to go in. Clone the data mart example template
|
||||
2. Datamart, can use one big one that has different tables for everything that we need.
|
||||
3. Local airflow that calls api and gets data. continuation tokens can be used. there is a good example
|
||||
4. Upload everything to microlise servers where it is run automatically
|
||||
5. Power bi, change data sources to be references to our sql. Can have all tables in power bi. Use filters
|
||||
|
||||
** Summary that I wrote in slack:
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DataMart.SQLServer: very useful link explaining what datamarts are, ELT (extract, load, transform) orchestration and how to set this up
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DataMart.DataEngineering.InsightReadiness: an example of what a datamart repo looks like. in the template repo, the two important files are tsqlt and DataMartTemplate (in this instance its called InsightReadiness )
|
||||
|
||||
- tsqlt coverage should be above 60%
|
||||
|
||||
- https://microliseuk.sharepoint.com/sites/msteams_bb7636: This link is the CoP (community of practice) for the airflow. You'll find useful information regarding what apache airflow is and how to set things up.
|
||||
|
||||
- https://microliseuk.sharepoint.com/sites/msteams_bb7636/SitePages/Getting-started-with-Airflow.aspx: This link tells you how to set airflow up locally. Once you're done, this will be published to an airflow scheduler platform. Keep in mind that when you want to publish this, document that it succeeds in the PR.
|
||||
|
||||
- http://airmastqa01.mms.local:8080/home: this link shows all the DAGs that are currently running in a testing environment, and when they are scheduled. Login using MSUP (edited)
|
||||
|
||||
- http://airmastprd01.mms.local:8080/home: This link is the prod version
|
||||
|
||||
- https://azdo.microlise.com/MicroliseCollection/Microlise/_git/DevDocs?path=/Airflow/HowTo/PowerBiReportRefresh.md&version=GBmain&_a=preview: Might prove to be useful when we do the powerbi refresh thing...
|
||||
25
backups/backlog.org~
Normal file
25
backups/backlog.org~
Normal file
@@ -0,0 +1,25 @@
|
||||
#+TITLE: Backlog
|
||||
#+OPTIONS: num:nil toc:nil
|
||||
#+DATE: <2025-11-08 Sat 11:08>
|
||||
#+FILETAGS: :emacs:website:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: backlog
|
||||
|
||||
* TODO
|
||||
|
||||
* DOING
|
||||
|
||||
* DONE
|
||||
|
||||
- Refactor the build script to extract functions into seperate files <2025-12-16 Tue>
|
||||
- Add a WIP property in an org file. If the header contains the string "WIP" then create a list of all WIP items and add to it. Otherwise do nothing.
|
||||
- Page for latest updated files.
|
||||
- Give lima extra controls, and update the syntax file for her.
|
||||
- Service for RDP
|
||||
- Create the backlog page.
|
||||
|
||||
* For another time :(
|
||||
|
||||
- Instead of deleting the output directory each time the website is built, only delete the files that have been changed.
|
||||
- Remove lima's files from the sitemap (for some odd reason).
|
||||
|
||||
25
backups/benefits-of-reading.org~
Normal file
25
backups/benefits-of-reading.org~
Normal file
@@ -0,0 +1,25 @@
|
||||
#+TITLE: Benefits of Reading
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-14 Thu 23:36>
|
||||
#+FILETAGS: :reading:insights:
|
||||
|
||||
* Benefits [fn:1]
|
||||
|
||||
At times, I find myself being able to digest material more quickly and efficiently, and so I thought to myself as to what the probable cause of this was. The answer transpired to be the inculcating of reading in my daily routine. As humans, we are constantly surrounded with information, some are noisy, some are useful. We can choose to /filter/ out noisy information by limiting exposure of their avenues, however the topic I wanted to briefly touch upon is what are the (cognitive and non-cognitive) benefits of increasing the intake of useful information through the medium of reading?
|
||||
|
||||
** Mental Stimulation
|
||||
The brain is a muscle, in order to keep any muscle strong and healthy, it needs stimulation and attention. Thus, reading is an excellent way of ensuring the brain is /fit and healthy/.
|
||||
|
||||
** Stress Reduction
|
||||
A well written novel or informative book can go a long way in making you forget about the worries of the world.
|
||||
|
||||
** Increase in Knowledge
|
||||
Reading will always fill your head with new information.
|
||||
|
||||
** Entertainment
|
||||
A plethora of genres can keep anyone entertained!
|
||||
|
||||
** Better writing skills
|
||||
Being exposed to different writing styles allows you to be influenced to obtaining your own unique writing style.
|
||||
|
||||
[fn:1] Check the following [[https://www.crawshawacademy.org.uk/seecmsfile/?id=165][link]]
|
||||
7
backups/career-intro.org~
Normal file
7
backups/career-intro.org~
Normal file
@@ -0,0 +1,7 @@
|
||||
#+TITLE: Career Introduction
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-11-06 Thu 21:29>
|
||||
#+FILETAGS: :introduction:
|
||||
#+WIP:
|
||||
|
||||
Here lies posts that relate to the technicalities and intricacies of my career. They will include information regarding my competencies, career growth, technical components and many other things. The goal is to have a single designated area where I can read, learn and relearn different things.
|
||||
127
backups/comments.js~
Normal file
127
backups/comments.js~
Normal file
@@ -0,0 +1,127 @@
|
||||
let pageSlug = null;
|
||||
|
||||
/* -----------------------------
|
||||
* DOM builders
|
||||
* ----------------------------- */
|
||||
|
||||
function createComment(comment) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "comment";
|
||||
wrapper.dataset.id = comment.id;
|
||||
|
||||
const author = document.createElement("strong");
|
||||
author.textContent = comment.author || "Anonymous";
|
||||
|
||||
const body = document.createElement("p");
|
||||
body.textContent = comment.content;
|
||||
|
||||
const time = document.createElement("time");
|
||||
time.className = "comment-date";
|
||||
time.dateTime = comment.created_at;
|
||||
time.textContent = new Date(comment.created_at).toLocaleString();
|
||||
|
||||
wrapper.appendChild(author);
|
||||
wrapper.appendChild(body);
|
||||
wrapper.appendChild(time);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
* Rendering
|
||||
* ----------------------------- */
|
||||
|
||||
function renderComments(comments) {
|
||||
const list = document.getElementById("comments-list");
|
||||
list.innerHTML = "";
|
||||
|
||||
if (!comments.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "comments-empty";
|
||||
empty.textContent = "No comments yet.";
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
comments.forEach(c => {
|
||||
list.appendChild(createComment(c));
|
||||
});
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
* API
|
||||
* ----------------------------- */
|
||||
|
||||
async function fetchComments() {
|
||||
const res = await fetch(`/api/comments/${pageSlug}`);
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch comments");
|
||||
return [];
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function postComment(author, content) {
|
||||
const res = await fetch("/api/comments", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
page_slug: pageSlug,
|
||||
author: author || null,
|
||||
content: content
|
||||
})
|
||||
});
|
||||
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Form handling
|
||||
* ----------------------------- */
|
||||
|
||||
function wireCommentForm() {
|
||||
const form = document.getElementById("comment-form");
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const authorInput = form.querySelector("input[name='author']");
|
||||
const textarea = form.querySelector("textarea[name='content']");
|
||||
|
||||
const author = authorInput.value.trim();
|
||||
const content = textarea.value.trim();
|
||||
|
||||
if (!content) return;
|
||||
|
||||
const ok = await postComment(author, content);
|
||||
if (ok) {
|
||||
textarea.value = "";
|
||||
loadComments(); // same pattern as your Kanban board
|
||||
} else {
|
||||
alert("Failed to post comment");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Entry point
|
||||
* ----------------------------- */
|
||||
|
||||
async function loadComments() {
|
||||
const comments = await fetchComments();
|
||||
renderComments(comments);
|
||||
}
|
||||
|
||||
function initComments() {
|
||||
const section = document.getElementById("comments");
|
||||
if (!section) return;
|
||||
|
||||
pageSlug = section.dataset.slug;
|
||||
|
||||
wireCommentForm();
|
||||
loadComments();
|
||||
}
|
||||
|
||||
initComments();
|
||||
168
backups/competency-status-board.js~
Normal file
168
backups/competency-status-board.js~
Normal file
@@ -0,0 +1,168 @@
|
||||
const STATES = [
|
||||
{ key: "completed", label: "Completed" },
|
||||
{ key: "manager_review", label: "Manager Review" },
|
||||
{ key: "in_progress", label: "In Progress" },
|
||||
{ key: "not_started", label: "Not Started" },
|
||||
{ key: "comments", label: "Comments" }
|
||||
];
|
||||
|
||||
function isMobile() {
|
||||
return window.matchMedia("(max-width: 600px)").matches;
|
||||
}
|
||||
|
||||
function populateMobileControls(items) {
|
||||
if (!isMobile()) return;
|
||||
|
||||
const panel = document.getElementById("mobile-move-panel");
|
||||
const itemSelect = document.getElementById("move-item");
|
||||
const fromSelect = document.getElementById("move-from");
|
||||
const toSelect = document.getElementById("move-to");
|
||||
|
||||
itemSelect.innerHTML = "<option value=''>Select competency</option>";
|
||||
fromSelect.innerHTML = "<option value=''>From</option>";
|
||||
toSelect.innerHTML = "<option value=''>To</option>";
|
||||
|
||||
items.forEach(item => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.title;
|
||||
opt.dataset.state = item.state;
|
||||
itemSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
STATES.forEach(s => {
|
||||
fromSelect.appendChild(new Option(s.label, s.key));
|
||||
toSelect.appendChild(new Option(s.label, s.key));
|
||||
});
|
||||
|
||||
// Auto-fill "from" when item selected
|
||||
itemSelect.onchange = () => {
|
||||
const selected = itemSelect.selectedOptions[0];
|
||||
if (selected?.dataset.state) {
|
||||
fromSelect.value = selected.dataset.state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("move-confirm").addEventListener("click", async () => {
|
||||
const itemId = document.getElementById("move-item").value;
|
||||
const from = document.getElementById("move-from").value;
|
||||
const to = document.getElementById("move-to").value;
|
||||
|
||||
if (!itemId || !to) {
|
||||
alert("Select an item and target column");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from === to) {
|
||||
alert("Item is already in that column");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${itemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: to })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard();
|
||||
} else {
|
||||
alert("Failed to move competency");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
let draggedItemId = null;
|
||||
|
||||
function countByState(items) {
|
||||
return items.reduce((acc, item) => {
|
||||
acc[item.state] = (acc[item.state] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "kanban-card";
|
||||
card.draggable = true;
|
||||
card.dataset.id = item.id;
|
||||
card.textContent = item.title;
|
||||
|
||||
card.addEventListener("dragstart", () => {
|
||||
draggedItemId = item.id;
|
||||
card.classList.add("dragging");
|
||||
});
|
||||
|
||||
card.addEventListener("dragend", () => {
|
||||
draggedItemId = null;
|
||||
card.classList.remove("dragging");
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createColumn(state, items, counts) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "kanban-column";
|
||||
col.dataset.state = state.key;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerHTML = `
|
||||
<span class="kanban-title">${state.label}</span>
|
||||
<span class="kanban-count">${counts[state.key] || 0}</span>
|
||||
`;
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "kanban-list";
|
||||
|
||||
list.addEventListener("dragover", e => e.preventDefault());
|
||||
|
||||
list.addEventListener("drop", async () => {
|
||||
if (!draggedItemId) return;
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${draggedItemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: state.key })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard(); // simple & safe re-render
|
||||
} else {
|
||||
alert("Failed to update state");
|
||||
}
|
||||
});
|
||||
|
||||
items
|
||||
.filter(i => i.state === state.key)
|
||||
.forEach(i => list.appendChild(createCard(i)));
|
||||
|
||||
col.appendChild(header);
|
||||
col.appendChild(list);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
function renderBoard(items) {
|
||||
const board = document.getElementById("kanban-board");
|
||||
board.innerHTML = "";
|
||||
|
||||
const counts = countByState(items);
|
||||
|
||||
STATES.forEach(state => {
|
||||
board.appendChild(createColumn(state, items, counts));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function loadBoard() {
|
||||
const res = await fetch("/api/competencies/items");
|
||||
const items = await res.json();
|
||||
renderBoard(items);
|
||||
populateMobileControls(items);
|
||||
}
|
||||
|
||||
|
||||
loadBoard();
|
||||
12
backups/countdown.org~
Normal file
12
backups/countdown.org~
Normal file
@@ -0,0 +1,12 @@
|
||||
#+TITLE: Countdown
|
||||
#+OPTIONS: num:nil title:nil
|
||||
#+DATE: <2025-08-04 Mon>
|
||||
|
||||
* zaine and halima :)
|
||||
|
||||
|
||||
#+ATTR_HTML: :class countdown-wrap
|
||||
{{{countdown(2026-08-02T00:00:00Z,Nikah Day)}}}
|
||||
|
||||
#+ATTR_HTML: :class countdown-wrap
|
||||
{{{countdown(2026-08-09T00:00:00Z,Walima Day)}}}
|
||||
138
backups/gallery-init.js~
Normal file
138
backups/gallery-init.js~
Normal file
@@ -0,0 +1,138 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (typeof window.BiggerPicture !== 'function') {
|
||||
console.error('[gallery-init] BiggerPicture not found. Check script path.');
|
||||
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);
|
||||
});
|
||||
|
||||
// 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; };
|
||||
|
||||
// 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();
|
||||
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) { enhanceSVG(containerEl); },
|
||||
onUpdate(containerEl){ enhanceSVG(containerEl); }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 4) If current slide is an SVG, swap to inline + enable svg-pan-zoom
|
||||
async function enhanceSVG(containerEl) {
|
||||
try {
|
||||
destroyPanZoom();
|
||||
|
||||
const imgEl = containerEl.querySelector('.bp-img img');
|
||||
if (!imgEl) return;
|
||||
|
||||
const src = imgEl.currentSrc || imgEl.src || '';
|
||||
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');
|
||||
holder.className = 'bp-svg-holder';
|
||||
holder.style.maxWidth = '95vw';
|
||||
holder.style.maxHeight = '95vh';
|
||||
htmlLayer.appendChild(holder);
|
||||
}
|
||||
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;
|
||||
|
||||
const svg = holder.querySelector('svg');
|
||||
if (!svg) { imgEl.style.visibility = ''; return; }
|
||||
|
||||
svg.style.maxWidth = '95vw';
|
||||
svg.style.maxHeight = '95vh';
|
||||
svg.style.display = 'block';
|
||||
|
||||
if (typeof window.svgPanZoom === 'function') {
|
||||
activePanZoom = svgPanZoom(svg, {
|
||||
zoomEnabled: true,
|
||||
controlIconsEnabled: true,
|
||||
fit: true,
|
||||
center: true,
|
||||
minZoom: 0.05,
|
||||
maxZoom: 400, // effectively "unlimited"
|
||||
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');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[gallery-init] SVG enhance failed:', err);
|
||||
}
|
||||
}
|
||||
});
|
||||
8
backups/hilberts.hotel.org~
Normal file
8
backups/hilberts.hotel.org~
Normal file
@@ -0,0 +1,8 @@
|
||||
#+TITLE: Hilbert's Hotel
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-11 Mon 18:39>
|
||||
#+FILETAGS: :maths:insights:
|
||||
|
||||
I recently stumbled across a thought experiment in mathematics, where the counterintuitive nature of infinity is portrayed. There is a hotel with an infinite number of rooms, all of which are occupied with a guest. There is a paradox upon the arrival of a new guest, the hotel is still shown to be able to accomodate this guest, despite it being previously coined as /full/.
|
||||
|
||||
See: [[https://plus.maths.org/content/hilberts-hotel][this link]] for more details on the full experimental thought and the proofs alongside.
|
||||
6
backups/invest-principles.org~
Normal file
6
backups/invest-principles.org~
Normal file
@@ -0,0 +1,6 @@
|
||||
#+title: Invest Principles
|
||||
#+date: <2025-11-06 Thu 21:08>
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+filetags: :learning:notes:
|
||||
|
||||
* TODO
|
||||
10
backups/lean.org~
Normal file
10
backups/lean.org~
Normal file
@@ -0,0 +1,10 @@
|
||||
#+title: Lean
|
||||
#+date: <2025-11-05 wed 20:46>
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+filetags: :learning:notes:
|
||||
#+WIP: t
|
||||
|
||||
* What Lean aims to do
|
||||
|
||||
Deliver more value with less waste by shortening feedback loops, improving flow, and continually learning.
|
||||
|
||||
18
backups/management-of-self.org~
Normal file
18
backups/management-of-self.org~
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: Management of self training
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-11-06 Thu 22:01>
|
||||
#+FILETAGS: :learning:notes:
|
||||
#+WIP:
|
||||
|
||||
Management of self in the workplace refers to how individuals regulate their behavior, emotions, and productivity to perform effectively and professionally. Here's a brief overview:
|
||||
|
||||
- *Time Management*: Prioritising tasks, meeting deadlines, and avoiding procrastination.
|
||||
- *Emotional Regulation*: Staying calm under pressure, handling stress, and maintaining a positive attitude.
|
||||
- *Accountability*: Taking responsibility for actions and outcomes, owning mistakes, and learning from them.
|
||||
- *Adaptability*: Being flexible with changes, open to feedback, and willing to improve.
|
||||
- *Professionalism*: Maintaining ethical standards, being respectful, and demonstrating integrity.
|
||||
- *Goal Setting*: Defining clear objectives and staying focused on achieving them.
|
||||
|
||||
It is important as it helps build trust, improve performance and creates a healthy work environment.
|
||||
|
||||
[[../../assets/images/stress-in-workplace.png]]
|
||||
11
backups/normalisation.org~
Normal file
11
backups/normalisation.org~
Normal file
@@ -0,0 +1,11 @@
|
||||
#+TITLE: Benefits of Normalisation
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-11-10 Mon 18:04>
|
||||
#+FILETAGS: :learning:notes:
|
||||
#+WIP:
|
||||
|
||||
For some reason it didn't include one of the bullet points, here it is:
|
||||
|
||||
- It simplifies maintenance, so it’s easier to update, delete or insert data without anomalies.
|
||||
|
||||
[[../../assets/images/competencies/Aware-of-the-Benefits-of-Normalisation.png]]
|
||||
0
backups/notes.js~
Normal file
0
backups/notes.js~
Normal file
38
backups/notes.org~
Normal file
38
backups/notes.org~
Normal file
@@ -0,0 +1,38 @@
|
||||
#+TITLE: Public Notes
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+NO_SIDENOTES: t
|
||||
#+COMMENTS: t
|
||||
#+SLUG: notes
|
||||
|
||||
|
||||
This page is a shared notes space — short thoughts, marginalia, and passing ideas.
|
||||
Names are required; everything else is intentionally lightweight.
|
||||
|
||||
#+BEGIN_EXPORT html
|
||||
<div id="notes-wall">
|
||||
<p>Loading notes…</p>
|
||||
</div>
|
||||
|
||||
<div id="notes-form-wrapper">
|
||||
<form id="notes-form">
|
||||
<label for="note-author">Name</label>
|
||||
<input
|
||||
id="note-author"
|
||||
name="author_name"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
required
|
||||
/>
|
||||
|
||||
<label for="note-content">Note</label>
|
||||
<textarea
|
||||
id="note-content"
|
||||
name="content"
|
||||
rows="4"
|
||||
required
|
||||
></textarea>
|
||||
|
||||
<button type="submit">Post note</button>
|
||||
</form>
|
||||
</div>
|
||||
#+END_EXPORT
|
||||
84
backups/owasp.org~
Normal file
84
backups/owasp.org~
Normal file
@@ -0,0 +1,84 @@
|
||||
#+TITLE: OWASP Top Ten
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-10-19 Sun 13:21>
|
||||
#+FILETAGS: :learning:notes:
|
||||
|
||||
* Competency notes:
|
||||
|
||||
[[../../assets/images/owasp/Screenshot 2025-10-16 134403.png]]
|
||||
[[../../assets/images/owasp/Screenshot 2025-10-16 134438.png]]
|
||||
[[../../assets/images/owasp/Screenshot 2025-10-16 134457.png]]
|
||||
[[../../assets/images/owasp/Screenshot 2025-10-16 134403.png]]
|
||||
[[../../assets/images/owasp/Screenshot 2025-10-16 134512.png]]
|
||||
|
||||
* OWASP Top 10 (2021)
|
||||
|
||||
** A01: Broken Access Control
|
||||
- Users can act outside their permissions (e.g., IDOR, privilege escalation).
|
||||
- Mitigations:
|
||||
- Enforce server-side authorization on every request.
|
||||
- Deny by default; allow only explicit permissions.
|
||||
- Test with role and privilege matrices.
|
||||
|
||||
** A02: Cryptographic Failures
|
||||
- Sensitive data is exposed due to weak or misused cryptography.
|
||||
- Mitigations:
|
||||
- Use modern TLS everywhere.
|
||||
- Encrypt sensitive data in transit and at rest.
|
||||
- Manage keys securely and avoid homegrown crypto.
|
||||
|
||||
** A03: Injection
|
||||
- Untrusted input alters queries or commands (SQLi, XSS, command injection).
|
||||
- Mitigations:
|
||||
- Use parameterized queries and prepared statements.
|
||||
- Sanitize and validate input.
|
||||
- Escape output appropriately for each context.
|
||||
|
||||
** A04: Insecure Design
|
||||
- Security flaws in system design or architecture.
|
||||
- Mitigations:
|
||||
- Perform threat modeling early.
|
||||
- Use secure design patterns and reference architectures.
|
||||
- Enforce defense-in-depth and least privilege.
|
||||
|
||||
** A05: Security Misconfiguration
|
||||
- Insecure defaults, unnecessary features, verbose errors, misconfigured services.
|
||||
- Mitigations:
|
||||
- Harden configurations and use secure defaults.
|
||||
- Automate configuration management.
|
||||
- Disable unused features and accounts.
|
||||
|
||||
** A06: Vulnerable and Outdated Components
|
||||
- Using outdated software or libraries with known vulnerabilities.
|
||||
- Mitigations:
|
||||
- Maintain an inventory (SBOM) of components.
|
||||
- Monitor CVEs and patch frequently.
|
||||
- Use trusted package sources.
|
||||
|
||||
** A07: Identification and Authentication Failures
|
||||
- Broken login, session management, or weak authentication.
|
||||
- Mitigations:
|
||||
- Use strong authentication (MFA recommended).
|
||||
- Protect session tokens; implement logout and timeouts.
|
||||
- Avoid exposing sensitive session IDs in URLs.
|
||||
|
||||
** A08: Software and Data Integrity Failures
|
||||
- Relying on untrusted software or pipelines without integrity verification.
|
||||
- Mitigations:
|
||||
- Sign and verify code and dependencies.
|
||||
- Secure CI/CD pipelines.
|
||||
- Use integrity checks for data and updates.
|
||||
|
||||
** A09: Security Logging and Monitoring Failures
|
||||
- Inadequate logging or monitoring prevents detection of attacks.
|
||||
- Mitigations:
|
||||
- Log critical events (auth, access, errors).
|
||||
- Centralize and monitor logs.
|
||||
- Establish alerting and response procedures.
|
||||
|
||||
** A10: Server-Side Request Forgery (SSRF)
|
||||
- The application fetches URLs from untrusted sources on behalf of users.
|
||||
- Mitigations:
|
||||
- Deny outbound network access by default.
|
||||
- Use allowlists for external requests.
|
||||
- Protect metadata and internal services.
|
||||
77
backups/probation-objectives.org~
Normal file
77
backups/probation-objectives.org~
Normal file
@@ -0,0 +1,77 @@
|
||||
#+TITLE: Probation Objectives:
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-12-08 Mon 17:55>
|
||||
#+filetags: :review:notes:
|
||||
#+WIP: t
|
||||
|
||||
* Checklists:
|
||||
** Customer Service Report
|
||||
- Introduction
|
||||
- Service Desk Experience
|
||||
- Customer Experience
|
||||
- Lessons Learnt
|
||||
- Conclusion
|
||||
|
||||
** Personal Development Plan
|
||||
- Create a SWOT
|
||||
- Create a Personal Development Plan with SMART objectives
|
||||
- Include strengths and weaknesses of your team role
|
||||
- Define short-, medium-, and long-term objectives
|
||||
- Keep traction on the competency tasks and dashboard
|
||||
|
||||
** Process Best Practice Report
|
||||
- Introduction
|
||||
- Lean Overview
|
||||
- Software Development Overview
|
||||
- Lessons Learnt
|
||||
- Conclusion
|
||||
|
||||
** Competencies
|
||||
- +20% End of Nov+
|
||||
- 20% End of Dec
|
||||
- 20% End of Jan
|
||||
|
||||
** Books
|
||||
- +The Toyota Way+
|
||||
- +The Clean Coder+
|
||||
- Agile Testing (in progress)
|
||||
- Code Complete 2
|
||||
- Clean Code (in progress)
|
||||
- Working Effectively with Legacy Code
|
||||
- Headfirst into Design Patterns
|
||||
|
||||
** Public Code Review (Demonstrate development best practice)
|
||||
- Training on appropriate languages (C#, PHP Laravel, JavaScript, SQL, PowerShell, NUnit, PHPUnit, Jest, tSQLt, pester)
|
||||
- Read books and do training on development best practices (SOLID, Design Patterns, Continuous Monitoring, TDD, BDD, CI/CD)
|
||||
- Training around Requirements (Feature writing, User Story writing, Task writing, User Story Mapping, Low Fidelity UI Designs)
|
||||
- Daily Kata’s and regular Dojo’s
|
||||
- Present software developed by the team in a Public Code Review
|
||||
|
||||
** Deployment Pipeline (Integrate a build and deployment pipeline)
|
||||
- Implement a TFS build and deployment pipeline with pester tests
|
||||
- Implement and maintain CI/CD
|
||||
- Continuously monitor environments daily
|
||||
- Build and/or maintain an environment using PowerShell, Vagrant, Ansible, and/or Terraform
|
||||
|
||||
** Misc
|
||||
- DevOps Skills for Developers with VS and TFS
|
||||
- Getting Started with Ansible
|
||||
- Introduction to Versioning Environments with Vagrant
|
||||
- Terraform – Getting Started
|
||||
|
||||
* Rotations
|
||||
Rotations start on: <2026-01-14 Wed>
|
||||
|
||||
* Probation meetings:
|
||||
1. <2025-11-06 Thu>
|
||||
2. <2025-12-11 Thu>
|
||||
3.
|
||||
|
||||
* Document:
|
||||
|
||||
[[../../assets/images/reviews/probation-objectives/probation-objectives-1.png]]
|
||||
[[../../assets/images/reviews/probation-objectives/probation-objectives-2.png]]
|
||||
[[../../assets/images/reviews/probation-objectives/probation-objectives-3.png]]
|
||||
[[../../assets/images/reviews/probation-objectives/probation-objectives-4.png]]
|
||||
[[../../assets/images/reviews/probation-objectives/probation-objectives-5.png]]
|
||||
|
||||
86
backups/publish-pages.org~
Normal file
86
backups/publish-pages.org~
Normal file
@@ -0,0 +1,86 @@
|
||||
#+TITLE: How to publish pages using Org Publish
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-08 Sat 10:57>
|
||||
#+filetags: :website:
|
||||
|
||||
* How to publish web pages using ~org-publish~
|
||||
|
||||
This website is heavily inspired by some people who have decided to use ~org-publish~ as a way to convert ~org~ files into ~html~. I came across a few that took the plunge and decided to migrate from platforms like Wordpress and instead opted for a more transparent, text-based workflow.
|
||||
|
||||
The Org website[fn:1] now looks a lot better than it used to.
|
||||
|
||||
There is a [[https://en.wikipedia.org/wiki/Unix_philosophy][Unix philosophy]] that emphasises building simple and compact code that can be easily maintainable, and following this approach (do one thing well) aligns with this philosophy.
|
||||
|
||||
So how does one go about publishing web pages through Emacs's ~org-publish~? The answer is quite simple. You probably will need to have some familiarity with ~elisp~[fn:2] although it's not a must.
|
||||
|
||||
The first thing we will want to do is edit our ~init.el~ file, which is the *initialization* file. In this file we want to add the following:
|
||||
|
||||
/This is a simple and quick way of getting this setup, although there is a better and more scalable approach to this, where we have a build script that runs each time we want to generate the project. See [[file:setup.org][here]] for more details./
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
|
||||
(setq org-publish-project-alist
|
||||
'(("org-notes"
|
||||
:base-directory "~/web"
|
||||
:base-extension "org"
|
||||
:publishing-directory "~/web"
|
||||
:recursive t
|
||||
:publishing-function org-html-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:with-toc t
|
||||
:section-numbers t
|
||||
:html-head "<link rel=\"stylesheet\" href=\"style.css\" />"
|
||||
)
|
||||
("org-static"
|
||||
:base-directory "~/web"
|
||||
:base-extension "css\\|js\\|png\\|jpg\\|gif"
|
||||
:publishing-directory "~/web/output"
|
||||
:recursive t
|
||||
:publishing-function org-publish-attachment)
|
||||
("website" :components ("org-notes" "org-static"))))
|
||||
|
||||
#+end_src
|
||||
|
||||
What this snippet of code is doing is it is telling ~org-publish~ what **configurations** we want to use when we end up running the publish command. Under ~~/web~ is where we want to include our files. Let us take a simple example and create an ~index.org~ file:
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
|
||||
,#+TITLE: Website Name
|
||||
,#+OPTIONS: toc:nil num:nil
|
||||
|
||||
,* Welcome!
|
||||
|
||||
This is a simple website
|
||||
|
||||
#+end_src
|
||||
|
||||
There are some metadata at the very top as we can see, these help us customise each page the way we want to. Here I disabled the Table of Contents and section numbers just for this file. We can also create a simple ~styles.css~ file in the same directory.
|
||||
After doing this, we can run the command:
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
|
||||
M-x org-publish RET website
|
||||
|
||||
#+end_src
|
||||
|
||||
This will generate a html file inside the ~~/web/output/~ directory, and we can serve this file using any static site generation tools; I recommend python due to it's simplicity:
|
||||
|
||||
#+begin_src bash
|
||||
|
||||
cd ~/web/output
|
||||
python3 -m http.server 8000
|
||||
|
||||
#+end_src
|
||||
|
||||
And there we go! The files are now being hosted on ~http://localhost:8000/~ and we have a fully functioning workflow for converting org files into a static website.
|
||||
|
||||
* Contact
|
||||
|
||||
Feel free to reach out on GitHub: https://github.com/zainezq
|
||||
|
||||
|
||||
[fn:1] See: https://orgmode.org/manual/Publishing.html
|
||||
|
||||
[fn:2] Emacs Lisp is a Lisp dialect made for Emacs. See: https://en.wikipedia.org/wiki/Emacs_Lisp
|
||||
75
backups/requirements-features.org~
Normal file
75
backups/requirements-features.org~
Normal file
@@ -0,0 +1,75 @@
|
||||
#+TITLE: Requirements, features, user stories, tasks, walking skeletons
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-11-06 Thu 22:01>
|
||||
#+FILETAGS: :learning:notes:
|
||||
#+WIP:
|
||||
|
||||
** Requirements
|
||||
- Definition: High-level descriptions of what the system must do or how it should behave.
|
||||
- Types:
|
||||
- *Functional*: Specific behaviors (e.g., “The system must allow users to reset passwords”).
|
||||
- *Non-functional*: Quality attributes (e.g., performance, security, usability).
|
||||
- Agile Context: Requirements are often evolving and are captured incrementally through user stories and feedback loops.
|
||||
|
||||
** Epic
|
||||
An epic is a high-level objective or initiative that’s too big to complete in a single sprint. It represents a major business goal or capability and is broken down into smaller deliverables:
|
||||
|
||||
- Purpose: Organise related work under a common theme.
|
||||
|
||||
- Scope: Broad and strategic; spans multiple sprints.
|
||||
|
||||
- Example: “Enable secure user access” could be an epic that includes login, signup, password reset, and multi-factor authentication.
|
||||
|
||||
“An epic serves to manage tasks. It's a defined body of work that is segmented into specific tasks (called ‘stories’) based on the needs/requests of customers or end-users"
|
||||
|
||||
** Features
|
||||
- Definition: Distinct pieces of functionality that deliver value to users.
|
||||
- Scope: Larger than user stories but smaller than epics.
|
||||
- Example: “User profile management” could be a feature encompassing login, profile editing, and avatar upload.
|
||||
|
||||
** User Stories
|
||||
- Definition: Short, simple descriptions of a feature told from the perspective of the user.
|
||||
- Format: “As a [user], I want [goal] so that [reason].”
|
||||
- Purpose: Helps teams focus on user needs and prioritize work.
|
||||
- Agile Role: Central to backlog grooming, sprint planning, and iterative delivery.
|
||||
|
||||
** Tasks
|
||||
- Definition: Technical or operational steps needed to implement a user story.
|
||||
- Granularity: Smallest unit of work, often assigned to individual team members.
|
||||
- Example: For a story like “User can upload avatar,” tasks might include:
|
||||
- Create upload UI
|
||||
- Validate image format
|
||||
- Store image in database
|
||||
|
||||
** Walking Skeleton
|
||||
- Definition: A minimal, end-to-end version of the system that includes the core architecture and basic functionality.
|
||||
- Purpose: Validates the system’s structure early and allows incremental development.
|
||||
- Agile Benefit: Enables early integration, testing, and feedback while avoiding big-bang delivery.
|
||||
|
||||
** Hierarchy Overview
|
||||
|
||||
| Level | Description | Agile Artifact |
|
||||
|----------------+----------------------------+------------------|
|
||||
| Vision | Broad business goals | Epics |
|
||||
| Feature | Functional chunks of value | Features |
|
||||
| User Need | Specific user goals | User Stories |
|
||||
| Implementation | Technical execution steps | Tasks |
|
||||
| Skeleton | Minimal working system | Walking Skeleton |
|
||||
|
||||
|
||||
Here is a summary of the agile hierarchy:
|
||||
|
||||
|
||||
| Level | Example | Description |
|
||||
|----------------+--------------------------------------+----------------------------------------|
|
||||
| **Epic** | Enable secure user access | Broad goal spanning multiple features |
|
||||
| **Feature** | User Login | Functional unit delivering user value |
|
||||
| **User Story** | “Log in with email/password” | Specific user goal within the feature |
|
||||
| **Task** | Build login form, connect to backend | Technical steps to implement the story |
|
||||
|
||||
* Example from work:
|
||||
<2025-10-09 Thu>
|
||||
The user story creation we did for a particular made up project (not the proudest but team voted on this sadly).
|
||||
|
||||
|
||||
[[../../assets/images/user_stories.jpg]]
|
||||
54
backups/retrospectives.org~
Normal file
54
backups/retrospectives.org~
Normal file
@@ -0,0 +1,54 @@
|
||||
#+title: Retrospectives
|
||||
#+date: <2025-11-05 wed 20:46>
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+filetags: :learning:notes:
|
||||
#+WIP:
|
||||
|
||||
|
||||
* Dates:
|
||||
- First retrospective meeting held at the end of sprint 1: <2025-11-04 Tue>
|
||||
- Second retrospective (and subsequent biweekly ones): <2025-11-18 Tue>
|
||||
|
||||
** Retros from <2025-12-02 Tue>
|
||||
[[../../assets/images/reviews/02-12-25-retros.jpg]]
|
||||
|
||||
|
||||
* Introduction:
|
||||
|
||||
|
||||
Retrospectives are part of the agile methodology of working, and is a way for the team to discuss and reflect on the way the sprint went.
|
||||
|
||||
** Purpose of Sprint Retrospectives
|
||||
- Continuous improvement: Teams inspect their work and processes to enhance quality and effectiveness.
|
||||
- Reflection and learning: Members discuss what went well, what didn’t, and why.
|
||||
- Actionable change: The goal is to identify specific improvements and implement them in the next sprint.
|
||||
|
||||
** Key Activities During a Retrospective
|
||||
- Review the sprint: Examine interactions, tools, processes, and outcomes.
|
||||
- Identify successes and challenges: Celebrate wins and analyse problems.
|
||||
- Generate insights: Explore root causes of issues and assumptions that led to missteps.
|
||||
- Plan improvements: Decide on changes to try in the next sprint, often adding them to the backlog.
|
||||
|
||||
** Who Participates
|
||||
- Entire Scrum Team: Product Owner, Scrum Master, and Developers.
|
||||
- Optional stakeholders if their input is valuable, but the focus remains on the team’s internal dynamics.
|
||||
|
||||
** Common Retrospective Techniques
|
||||
- Start, Stop, Continue: What to begin, cease, or keep doing.
|
||||
- Mad, Sad, Glad: Emotional reflection on the sprint.
|
||||
- 4Ls (Liked, Learned, Lacked, Longed for): A deeper dive into team sentiment and learning.
|
||||
|
||||
** Timing and Format
|
||||
- Held at the end of each sprint, typically lasting 60–90 minutes.
|
||||
- Can be in-person, remote, or hybrid, using tools like sticky notes, voting widgets, and templates.
|
||||
|
||||
** Best Practices
|
||||
- Keep it constructive and focused.
|
||||
- Ensure psychological safety so team members feel comfortable sharing.
|
||||
- Follow through on action items to avoid stagnation and disillusionment.
|
||||
|
||||
* Frameworks used
|
||||
|
||||
Boat model:
|
||||
|
||||
[[../../assets/images/retrospective_boat_model.png]]
|
||||
21
backups/services.org~
Normal file
21
backups/services.org~
Normal file
@@ -0,0 +1,21 @@
|
||||
#+TITLE: Service
|
||||
#+OPTIONS: toc:nil num:nil title:nil
|
||||
|
||||
| Service | Link |
|
||||
|---------------+--------------|
|
||||
| PgAdmin | [[https://pgadmin.zainezq.com/][PgAdmin]] |
|
||||
| Stirling PDF | [[https://stirlingpdf.zainezq.com/][Stirling PDF]] |
|
||||
| Portainer | [[https://portainer.zainezq.com/][Portainer]] |
|
||||
| File Browser | [[https://filebrowser.zainezq.com/][File Browser]] |
|
||||
| Calibre Web | [[https://calibre.zainezq.com/][Calibre Web]] |
|
||||
| Miniflux | [[https://miniflux.zainezq.com/][Miniflux]] |
|
||||
| JupyterLab | [[https://jupyter.zainezq.com/][JupyterLab]] |
|
||||
| Guacamole | [[https://guac.zainezq.com/][Guacamole]] |
|
||||
| Vaultwarden | [[https://bitwarden.zainezq.com/][Vaultwarden]] |
|
||||
| Reader | [[https://reader.zainezq.com/][Reader]] |
|
||||
| Netdata | [[https://netdata.zainezq.com/][Netdata]] |
|
||||
| Books VNC | [[https://booksvnc.zainezq.com/][Books VNC]] |
|
||||
| Actual | [[https://actual.zainezq.com/][Actual]] |
|
||||
| Photo Library | [[https://photo.zainezq.com/][Immich]] |
|
||||
| VPN | [[https://wgeasy.zainezq.com/][wg-easy]] |
|
||||
| Glance | [[https://glance.zainezq.com/][Glance]] |
|
||||
684
backups/setup.org~
Normal file
684
backups/setup.org~
Normal file
@@ -0,0 +1,684 @@
|
||||
#+TITLE: Setup
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+COMMENTS: t
|
||||
#+SLUG: setup
|
||||
|
||||
* Introduction
|
||||
|
||||
/Last updated: <2025-11-06 Thu 21:16>/
|
||||
|
||||
See this link[fn:1] for a more generic introduction on how to publish websites using ~org-publish~.
|
||||
|
||||
This page will highlight the ~build-script.el~ used to generate this website. This entire website is generated through org files using the command:
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
emacs -Q --script build-site.el
|
||||
#+END_SRC
|
||||
|
||||
* The script
|
||||
|
||||
The script is as follows, at the start I have some metadata relating to the file, followed by package management,then the declaration of variables/functions and finally the ~org-publish-project-alist~ which handles nearly all of the project generation instructions.
|
||||
|
||||
/The following code snippet will probably change often, as I can never seem to stick to a single configuration 😀/
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;;; build-site.el --- Publish my website using org-publish -*- lexical-binding: t; -*-
|
||||
|
||||
;; Author: Zaine Qayyum <zaineulabideen@outlook.com>
|
||||
;; Created: 2025-08-08
|
||||
;; Purpose: Build and publish my static site from Org files.
|
||||
|
||||
;;; Commentary:
|
||||
;; Run this file with:
|
||||
;; emacs -Q --script build-site.el
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'package)
|
||||
(setq package-user-dir (expand-file-name "./.packages"))
|
||||
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
|
||||
("elpa" . "https://elpa.gnu.org/packages/")))
|
||||
|
||||
(require 'ox-publish)
|
||||
(require 'cl-lib)
|
||||
(require 'org)
|
||||
(require 'ox)
|
||||
(require 'ox-html)
|
||||
|
||||
;; Initialize the package system
|
||||
(package-initialize)
|
||||
(unless package-archive-contents
|
||||
(package-refresh-contents))
|
||||
|
||||
;; Install dependencies
|
||||
(package-install 'htmlize)
|
||||
(add-to-list 'load-path "~/master-folder/org_files/org_web/")
|
||||
(require 'htmlize)
|
||||
|
||||
(setq org-html-htmlize-output-type 'css)
|
||||
|
||||
(defvar z-shared-head
|
||||
"
|
||||
<link rel=\"stylesheet\" href=\"/assets/styles/style.css\" />
|
||||
<link rel=\"stylesheet\" href=\"/assets/styles/bigger-picture.min.css\" />
|
||||
|
||||
<script src=\"/assets/scripts/script.js\" defer></script>
|
||||
<script src=\"/assets/scripts/bigger-picture.min.js\" defer></script>
|
||||
<script src=\"/assets/scripts/svg-pan-zoom.min.js\" defer></script>
|
||||
<script src=\"/assets/scripts/gallery-init.js\" defer></script>
|
||||
"
|
||||
)
|
||||
|
||||
(setq org-export-global-macros
|
||||
(append
|
||||
'(("sidenote"
|
||||
. "@@html:<label for=\"sn$1\" class=\"margin-toggle sidenote-number\"></label><input type=\"checkbox\" id=\"sn$1\" class=\"margin-toggle\"/><span class=\"sidenote\">$2</span>@@")
|
||||
("epigraph" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2</footer></blockquote></div>@@")
|
||||
("epigraph_single" . "@@html:<div class=\"epigraph\"><blockquote>$1</blockquote></div>@@")
|
||||
("epigraph3" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2, <cite>$3</cite></footer></blockquote></div>@@")
|
||||
("kbd" . "@@html:<kbd>$1</kbd>@@@@latex:\\texttt{$1}@@")
|
||||
("margimg"
|
||||
. "@@html:<aside class=\"marginnote\"><figure class=\"mn-fig\"><img src=\"$1\" alt=\"$2\" class=\"mn-img\" loading=\"lazy\" decoding=\"async\"/>$3</figure></aside>@@")
|
||||
("countdown" . "@@html:<time class=\"countdown\" datetime=\"$1\" data-label=\"$2\"></time>@@")
|
||||
|
||||
|
||||
org-export-global-macros)))
|
||||
|
||||
|
||||
(defvar z-preamble
|
||||
"
|
||||
<div class=\"banner-header\">
|
||||
<a href=\"/\"> <img src=\"/assets/images/gr.png\" alt=\"Site Logo\" class=\"banner-logo\" /> </a>
|
||||
<nav>
|
||||
<a href=\"/\">Home | </a>
|
||||
<a href=\"/posts/posts-list.html\">Posts | </a>
|
||||
<a href=\"/blogs/blogs-list.html\">Blogs | </a>
|
||||
<a href=\"/contact.html\">Contact | </a>
|
||||
<a href=\"/services.html\">Services</a>
|
||||
</nav>
|
||||
<button class=\"theme-toggle\" id=\"theme-toggle\" type=\"button\" aria-label=\"Toggle dark mode\">🌗 Theme</button>
|
||||
</div>
|
||||
<div id=\"updated\">Updated: %C</div>
|
||||
|
||||
"
|
||||
)
|
||||
|
||||
(defvar z-postamble
|
||||
"<footer>
|
||||
<div class=\"copyright-container\">
|
||||
<div class=\"copyright\">
|
||||
Copyright © 2022-2025 Zaine Qayyum. All rights reserved unless otherwise noted.</div></div>
|
||||
<div class=\"generated\">
|
||||
Created with %c on <a href=\"https://www.archlinux.org/\">Arch</a> <a href=\"https://www.gnu.org\">GNU</a>/<a href=\"https://www.kernel.org/\">Linux</a>
|
||||
</div>
|
||||
</footer>")
|
||||
|
||||
|
||||
(defun z/posts-sitemap (title list)
|
||||
"sitemap that lists post links as bullet points with dates and tags."
|
||||
(concat
|
||||
"#+TITLE: " title "\n"
|
||||
"#+OPTIONS: toc:nil num:nil \n\n"
|
||||
"See the categories: @@html:<a href=\"../categories.html\">Categories</a>@@\n\n"
|
||||
"* Posts:\n"
|
||||
(mapconcat
|
||||
(lambda (entry)
|
||||
(let* ((link (car entry))
|
||||
;; extract relative file name from the link
|
||||
(filename (if (string-match "\\[\\[file:\\([^]]+\\)\\]" link)
|
||||
(match-string 1 link)
|
||||
link))
|
||||
(full-path (expand-file-name filename "~/master-folder/org_files/org_web/posts/"))
|
||||
(date-str "no date")
|
||||
(tags-str ""))
|
||||
;; Get publish date
|
||||
(let ((date (org-publish-find-date full-path org-publish-project-alist)))
|
||||
(when date
|
||||
(setq date-str (format-time-string "%d-%m-%Y %H:%M" date))))
|
||||
;; Get FILETAGS from file buffer
|
||||
(when (file-exists-p full-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents full-path)
|
||||
(org-mode)
|
||||
(let* ((tags (cadr (assoc "FILETAGS" (org-collect-keywords '("FILETAGS"))))))
|
||||
(when tags
|
||||
(setq tags-str (mapconcat (lambda (tag)
|
||||
(format "@@html:<a href=\"/tags/%s.html\"> <span class=\"post-tag\">%s</span> </a>@@" tag tag))
|
||||
(split-string tags ":" t) ;; <- Splits by ":" and removes empty strings
|
||||
" "))))))
|
||||
;; Final line output
|
||||
(format "- %s @@html:<span class=\"post-date\">%s</span>@@ %s" link date-str tags-str)))
|
||||
(cdr list)
|
||||
"\n")))
|
||||
|
||||
|
||||
(defun z/blogs-sitemap (title list)
|
||||
"sitemap that lists blog links as bullet points with dates and tags."
|
||||
(concat
|
||||
"#+TITLE: " title "\n"
|
||||
"#+OPTIONS: toc:nil num:nil \n\n"
|
||||
"See the categories: @@html:<a href=\"../categories.html\">Categories</a>@@\n\n"
|
||||
"* Blogs:\n"
|
||||
(mapconcat
|
||||
(lambda (entry)
|
||||
(let* ((link (car entry))
|
||||
;; extract relative file name from the link
|
||||
(filename (if (string-match "\\[\\[file:\\([^]]+\\)\\]" link)
|
||||
(match-string 1 link)
|
||||
link))
|
||||
(full-path (expand-file-name filename "~/master-folder/org_files/org_web/blogs/"))
|
||||
(date-str "no date")
|
||||
(tags-str ""))
|
||||
;; Get publish date
|
||||
(let ((date (org-publish-find-date full-path org-publish-project-alist)))
|
||||
(when date
|
||||
(setq date-str (format-time-string "%d-%m-%Y %H:%M" date))))
|
||||
;; Get FILETAGS from file buffer
|
||||
(when (file-exists-p full-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents full-path)
|
||||
(org-mode)
|
||||
(let* ((tags (cadr (assoc "FILETAGS" (org-collect-keywords '("FILETAGS"))))))
|
||||
(when tags
|
||||
(setq tags-str (mapconcat (lambda (tag)
|
||||
(format "@@html:<a href=\"/tags/%s.html\"> <span class=\"post-tag\">%s</span> </a>@@" tag tag))
|
||||
(split-string tags ":" t) ;; <- Splits by ":" and removes empty strings
|
||||
" "))))))
|
||||
;; Final line output
|
||||
(format "- %s @@html:<span class=\"post-date\">%s</span>@@ %s" link date-str tags-str)))
|
||||
(cdr list)
|
||||
"\n")))
|
||||
|
||||
|
||||
(defun z/books-sitemap (title list)
|
||||
"sitemap that lists books."
|
||||
(concat
|
||||
"#+TITLE: " title "\n"
|
||||
"#+OPTIONS: toc:nil num:nil \n\n"
|
||||
"See the categories: @@html:<a href=\"../categories.html\">Categories</a>@@\n\n"
|
||||
"* Book Notes:\n"
|
||||
(mapconcat
|
||||
(lambda (entry)
|
||||
(let* ((link (car entry))
|
||||
;; extract relative file name from the link
|
||||
(filename (if (string-match "\\[\\[file:\\([^]]+\\)\\]" link)
|
||||
(match-string 1 link)
|
||||
link))
|
||||
(full-path (expand-file-name filename "~/master-folder/org_files/org_web/books/"))
|
||||
(date-str "no date")
|
||||
(tags-str ""))
|
||||
;; Get publish date
|
||||
(let ((date (org-publish-find-date full-path org-publish-project-alist)))
|
||||
(when date
|
||||
(setq date-str (format-time-string "%d-%m-%Y %H:%M" date))))
|
||||
;; Get FILETAGS from file buffer
|
||||
(when (file-exists-p full-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents full-path)
|
||||
(org-mode)
|
||||
(let* ((tags (cadr (assoc "FILETAGS" (org-collect-keywords '("FILETAGS"))))))
|
||||
(when tags
|
||||
(setq tags-str (mapconcat (lambda (tag)
|
||||
(format "@@html:<a href=\"/tags/%s.html\"> <span class=\"post-tag\">%s</span> </a>@@" tag tag))
|
||||
(split-string tags ":" t) ;; <- Splits by ":" and removes empty strings
|
||||
" "))))))
|
||||
;; Final line output
|
||||
(format "- %s @@html:<span class=\"post-date\">%s</span>@@ %s" link date-str tags-str)))
|
||||
(cdr list)
|
||||
"\n")))
|
||||
|
||||
(defun z/2025-sitemap (title list)
|
||||
"Sitemap that lists 2025 posts grouped by month, with dates and FILETAGS."
|
||||
(let ((output (concat
|
||||
"#+TITLE: " title "\n"
|
||||
"#+OPTIONS: toc:nil num:nil \n\n"
|
||||
"See the categories: @@html:<a href=\"../../categories.html\">Categories</a>@@\n\n"
|
||||
"* 2025\n"))
|
||||
(current-month nil))
|
||||
|
||||
;; `list` is what org-publish passes in; we skip its car (top-level title node)
|
||||
(dolist (entry (cdr list))
|
||||
;; In this setup each ENTRY is usually (LINK . OTHER-STUFF)
|
||||
(when (consp entry)
|
||||
(let* ((link (car entry))
|
||||
;; Extract relative filename from the [[file:...][...]] link
|
||||
(filename (if (string-match "\\[\\[file:\\([^]]+\\)\\]" link)
|
||||
(match-string 1 link)
|
||||
link))
|
||||
(full-path (expand-file-name
|
||||
filename
|
||||
"~/master-folder/org_files/org_web/blogs/2025/"))
|
||||
(date (when (file-exists-p full-path)
|
||||
(org-publish-find-date full-path org-publish-project-alist)))
|
||||
(date-str (if date
|
||||
(format-time-string "%d-%m-%Y %H:%M" date)
|
||||
"no date"))
|
||||
(month-str (if date
|
||||
(format-time-string "%B %Y" date) ; e.g. "August 2025"
|
||||
"No date"))
|
||||
(tags-str ""))
|
||||
|
||||
;; Collect FILETAGS from the file
|
||||
(when (file-exists-p full-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents full-path)
|
||||
(org-mode)
|
||||
(let ((tags (cadr (assoc "FILETAGS"
|
||||
(org-collect-keywords '("FILETAGS"))))))
|
||||
(when tags
|
||||
(setq tags-str
|
||||
(mapconcat
|
||||
(lambda (tag)
|
||||
(format "@@html:<a href=\"/tags/%s.html\"> \
|
||||
<span class=\"post-tag\">%s</span> </a>@@" tag tag))
|
||||
(split-string tags ":" t)
|
||||
" "))))))
|
||||
|
||||
;; Insert month heading whenever month changes
|
||||
(unless (equal month-str current-month)
|
||||
(setq current-month month-str)
|
||||
(setq output (concat output "\n** " month-str "\n")))
|
||||
|
||||
;; Insert the actual entry
|
||||
(setq output
|
||||
(concat output
|
||||
(format "- %s @@html:<span class=\"post-date\">%s</span>@@ %s\n"
|
||||
link date-str tags-str))))))
|
||||
|
||||
output))
|
||||
|
||||
(defun z/career-sitemap (title list)
|
||||
"Sitemap that lists careers posts grouped by month, with dates and FILETAGS."
|
||||
(let ((output (concat
|
||||
"#+TITLE: " title "\n"
|
||||
"#+OPTIONS: toc:nil num:nil \n\n"
|
||||
"See the categories: @@html:<a href=\"../../categories.html\">Categories</a>@@\n\n"
|
||||
"* 2025\n"
|
||||
"See the following page for more details: @@html:<a href=\"./career-intro.html\">Career Intro</a>@@\n"
|
||||
))
|
||||
(current-month nil))
|
||||
|
||||
;; `list` is what org-publish passes in; we skip its car (top-level title node)
|
||||
(dolist (entry (cdr list))
|
||||
;; In this setup each ENTRY is usually (LINK . OTHER-STUFF)
|
||||
(when (consp entry)
|
||||
(let* ((link (car entry))
|
||||
;; Extract relative filename from the [[file:...][...]] link
|
||||
(filename (if (string-match "\\[\\[file:\\([^]]+\\)\\]" link)
|
||||
(match-string 1 link)
|
||||
link))
|
||||
(full-path (expand-file-name
|
||||
filename
|
||||
"~/master-folder/org_files/org_web/posts/career/"))
|
||||
(date (when (file-exists-p full-path)
|
||||
(org-publish-find-date full-path org-publish-project-alist)))
|
||||
(date-str (if date
|
||||
(format-time-string "%d-%m-%Y %H:%M" date)
|
||||
"no date"))
|
||||
(month-str (if date
|
||||
(format-time-string "%B %Y" date) ; e.g. "August 2025"
|
||||
"No date"))
|
||||
(tags-str ""))
|
||||
|
||||
;; Collect FILETAGS from the file
|
||||
(when (file-exists-p full-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents full-path)
|
||||
(org-mode)
|
||||
(let ((tags (cadr (assoc "FILETAGS"
|
||||
(org-collect-keywords '("FILETAGS"))))))
|
||||
(when tags
|
||||
(setq tags-str
|
||||
(mapconcat
|
||||
(lambda (tag)
|
||||
(format "@@html:<a href=\"/tags/%s.html\"> \
|
||||
<span class=\"post-tag\">%s</span> </a>@@" tag tag))
|
||||
(split-string tags ":" t)
|
||||
" "))))))
|
||||
|
||||
;; Insert month heading whenever month changes
|
||||
(unless (equal month-str current-month)
|
||||
(setq current-month month-str)
|
||||
(setq output (concat output "\n** " month-str "\n")))
|
||||
|
||||
;; Insert the actual entry
|
||||
(setq output
|
||||
(concat output
|
||||
(format "- %s @@html:<span class=\"post-date\">%s</span>@@ %s\n"
|
||||
link date-str tags-str))))))
|
||||
|
||||
output))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(defun z/categories-sitemap (title _list)
|
||||
"Generate a categories page by scanning tags across org-posts and org-blogs."
|
||||
(let* ((expanded (org-publish-expand-projects org-publish-project-alist))
|
||||
(posts (assoc "org-posts" expanded))
|
||||
(blogs (assoc "org-blogs" expanded))
|
||||
;; DO NOT set :exclude to "" — it excludes everything
|
||||
(files (cl-remove-duplicates
|
||||
(append (and posts (org-publish-get-base-files posts))
|
||||
(and blogs (org-publish-get-base-files blogs)))
|
||||
:test #'file-equal-p))
|
||||
;; optional: drop the generated sitemaps
|
||||
(files (cl-remove-if
|
||||
(lambda (f)
|
||||
(member (file-name-nondirectory f)
|
||||
'("posts-list.org" "blogs-list.org" "sitemap.org" "categories.org")))
|
||||
files))
|
||||
(counts (make-hash-table :test 'equal)))
|
||||
;;(message "files I will scan: %S" files)
|
||||
(dolist (f files)
|
||||
(when (file-readable-p f)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents f)
|
||||
(org-mode)
|
||||
(let* ((kw (org-collect-keywords '("FILETAGS" "TAGS")))
|
||||
(raw (car (or (cdr (assoc "FILETAGS" kw))
|
||||
(cdr (assoc "TAGS" kw))))))
|
||||
(when raw
|
||||
(dolist (tag (split-string raw ":" t))
|
||||
(puthash tag (1+ (gethash tag counts 0)) counts)))))))
|
||||
|
||||
(let (tags)
|
||||
(maphash (lambda (k _) (push k tags)) counts)
|
||||
(setq tags (sort tags #'string-lessp))
|
||||
(concat
|
||||
"#+TITLE: " title "\n#+OPTIONS: toc:nil num:nil title:nil\n\n* Categories (Includes both blogs and posts)\n"
|
||||
(if tags
|
||||
(mapconcat
|
||||
(lambda (tag)
|
||||
(format "- [[file:tags/%s.org][@@html:<span class=\"post-tag\">%s</span>@@]] (%d)"
|
||||
(z/tag-slug tag) tag (gethash tag counts))
|
||||
)
|
||||
tags
|
||||
"\n")
|
||||
"_No tags found yet._")))))
|
||||
|
||||
|
||||
(defun z/tag-slug (s)
|
||||
"Turn a tag into a safe filename."
|
||||
(let ((down (downcase s)))
|
||||
(replace-regexp-in-string "[^a-z0-9]+" "-" down)))
|
||||
|
||||
(defun z/collect-post-files ()
|
||||
"Return all .org files from org-posts and org-blogs."
|
||||
(let* ((expanded (org-publish-expand-projects org-publish-project-alist))
|
||||
(posts (assoc "org-posts" expanded))
|
||||
(blogs (assoc "org-blogs" expanded)))
|
||||
(cl-remove-duplicates
|
||||
(append (and posts (org-publish-get-base-files posts))
|
||||
(and blogs (org-publish-get-base-files blogs)))
|
||||
:test #'file-equal-p)))
|
||||
|
||||
(defun z/gather-tag-index ()
|
||||
"Return hash: tag -> list of (FILE TITLE DATE-ISO)."
|
||||
(let ((idx (make-hash-table :test 'equal)))
|
||||
(dolist (f (z/collect-post-files))
|
||||
(when (and (string-match-p "\\.org\\'" f)
|
||||
(file-readable-p f)
|
||||
;; ignore generated lists
|
||||
(not (member (file-name-nondirectory f)
|
||||
'("posts-list.org" "blogs-list.org" "sitemap.org" "categories.org"))))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents f)
|
||||
(org-mode)
|
||||
(let* ((kw (org-collect-keywords '("TITLE" "FILETAGS" "TAGS" "DATE")))
|
||||
(title (or (car (cdr (assoc "TITLE" kw)))
|
||||
(file-name-base f)))
|
||||
(date (or (car (cdr (assoc "DATE" kw))) "")) ;; optional
|
||||
(raw (car (or (cdr (assoc "FILETAGS" kw))
|
||||
(cdr (assoc "TAGS" kw))))))
|
||||
(when raw
|
||||
(dolist (tag (split-string raw ":" t))
|
||||
(push (list f title date) (gethash tag idx))))))))
|
||||
idx))
|
||||
|
||||
(defun z/write-tag-pages ()
|
||||
"Generate tags/*.org pages listing posts for each tag."
|
||||
(let* ((site-root (expand-file-name "~/master-folder/org_files/org_web/"))
|
||||
(tags-dir (expand-file-name "tags" site-root)))
|
||||
(unless (file-directory-p tags-dir)
|
||||
(make-directory tags-dir t))
|
||||
(let ((idx (z/gather-tag-index)))
|
||||
(maphash
|
||||
(lambda (tag items)
|
||||
(let* ((slug (z/tag-slug tag))
|
||||
(outfile (expand-file-name (format "%s.org" slug) tags-dir)))
|
||||
(with-temp-file outfile
|
||||
(insert (format "#+TITLE: Tag: %s\n#+OPTIONS: toc:nil num:nil title:nil \n\n* Posts tagged %s\n"
|
||||
tag tag))
|
||||
;; sort newest first if DATE present
|
||||
(setq items (sort items (lambda (a b) (string> (nth 2 a) (nth 2 b)))))
|
||||
(dolist (it items)
|
||||
(let* ((file (nth 0 it))
|
||||
(title (nth 1 it))
|
||||
(rel (file-relative-name file tags-dir)))
|
||||
;; link to the source .org; org-publish will rewrite to the .html
|
||||
(insert (format "- [[file:%s][%s]]\n" rel title)))))))
|
||||
idx)
|
||||
)))
|
||||
|
||||
(defun z/filetags-html (info)
|
||||
"Return an HTML snippet for FILETAGS from INFO, or nil if none."
|
||||
(let ((tags (plist-get info :filetags)))
|
||||
(when tags
|
||||
(format
|
||||
"<div class=\"filetags\">%s</div>\n"
|
||||
(mapconcat (lambda (tag)
|
||||
(format "<a href=\"/categories.html\"> <span class=\"post-tag\">%s</span> </a>" tag))
|
||||
tags " ")))))
|
||||
|
||||
(defun z/insert-filetags-after-title (output backend info)
|
||||
"Insert FILETAGS after the first <h1 class=\"title\"> in OUTPUT."
|
||||
(if (org-export-derived-backend-p backend 'html)
|
||||
(let ((block (z/filetags-html info)))
|
||||
(if (and block
|
||||
(string-match "\\(<h1[^>]*class=[\"']title[\"'][^>]*>.*?</h1>\\)" output))
|
||||
(replace-match (concat "\\1\n" block) t nil output)
|
||||
output))
|
||||
output))
|
||||
|
||||
(org-export-define-derived-backend 'z-html 'html
|
||||
:filters-alist '((:filter-final-output . z/insert-filetags-after-title)))
|
||||
|
||||
(defun z/z-publish-to-html (plist filename pub-dir)
|
||||
"Publish FILENAME to HTML using the z-html backend."
|
||||
(org-publish-org-to 'z-html filename ".html" plist pub-dir))
|
||||
|
||||
|
||||
;; Define the publishing project
|
||||
(setq org-publish-project-alist
|
||||
`(("org-main"
|
||||
:recursive t
|
||||
:base-directory "~/master-folder/org_files/org_web/"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output"
|
||||
:base-extension "org"
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "sitemap.org"
|
||||
:sitemap-title "Sitemap"
|
||||
:sitemap-sort-files chronologically
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
|
||||
("org-categories-sitemap"
|
||||
:recursive t
|
||||
:base-directory "~/master-folder/org_files/org_web/"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output"
|
||||
:base-extension "org"
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "categories.org"
|
||||
:sitemap-title "Categories"
|
||||
:sitemap-function z/categories-sitemap
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-posts"
|
||||
:base-directory "~/master-folder/org_files/org_web/posts"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/posts/"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:with-toc t
|
||||
:section-numbers t
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "posts-list.org"
|
||||
:sitemap-title "Posts List"
|
||||
:sitemap-style list
|
||||
:sitemap-function z/posts-sitemap
|
||||
:sitemap-sort-files anti-chronologically
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-blogs"
|
||||
:base-directory "~/master-folder/org_files/org_web/blogs"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/blogs/"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "blogs-list.org"
|
||||
:sitemap-title "Blogs List"
|
||||
:sitemap-style list
|
||||
:sitemap-function z/blogs-sitemap
|
||||
:sitemap-sort-files anti-chronologically
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-books"
|
||||
:base-directory "~/master-folder/org_files/org_web/books/"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/books/"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "books-list.org"
|
||||
:sitemap-title "Books List"
|
||||
:sitemap-style list
|
||||
:sitemap-function z/books-sitemap
|
||||
:sitemap-sort-files anti-chronologically
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-2025"
|
||||
:base-directory "~/master-folder/org_files/org_web/blogs/2025/"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/blogs/2025/"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "2025-list.org"
|
||||
:sitemap-title "2025 List"
|
||||
:sitemap-style list
|
||||
:sitemap-function z/2025-sitemap
|
||||
:sitemap-sort-files anti-chronologically
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-career"
|
||||
:base-directory "~/master-folder/org_files/org_web/posts/career/"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/posts/career/"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function z/z-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-validation-link nil
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:auto-sitemap t
|
||||
:sitemap-filename "career-list.org"
|
||||
:sitemap-title "Career List"
|
||||
:sitemap-style list
|
||||
:sitemap-function z/career-sitemap
|
||||
:sitemap-sort-files anti-chronologically
|
||||
:html-head ,z-shared-head
|
||||
)
|
||||
("org-tags"
|
||||
:base-directory "~/master-folder/org_files/org_web/tags"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/tags"
|
||||
:recursive t
|
||||
:base-extension "org"
|
||||
:publishing-function org-html-publish-to-html
|
||||
:with-author nil
|
||||
:with-creator nil
|
||||
:html-preamble ,z-preamble
|
||||
:html-postamble ,z-postamble
|
||||
:html-head ,z-shared-head)
|
||||
("org-assets"
|
||||
:base-directory "~/master-folder/org_files/org_web/assets/"
|
||||
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|svg\\|pdf\\|woff\\|woff2\\|ttf"
|
||||
:publishing-directory "~/master-folder/org_files/org_web/output/assets/"
|
||||
:recursive t
|
||||
:publishing-function org-publish-attachment)
|
||||
))
|
||||
|
||||
(delete-directory "~/master-folder/org_files/org_web/output/" t)
|
||||
(delete-directory "~/master-folder/org_files/org_web/tags/" t)
|
||||
(message "Directory deleted")
|
||||
|
||||
(z/write-tag-pages)
|
||||
(org-publish-all t)
|
||||
|
||||
(message "Build complete!")
|
||||
|
||||
;;; build-site.el ends here
|
||||
|
||||
#+END_SRC
|
||||
|
||||
* Publish Script
|
||||
|
||||
As the project is hosted on Github, I have created a small script that is able to push changes to the remote repository, which Cloudflare will automatically detect and rebuild the website:
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
# Exit immediately if any command fails
|
||||
set -e
|
||||
|
||||
# Define paths
|
||||
ORG_OUTPUT_DIR="$HOME/master-folder/org_files/org_web/output"
|
||||
|
||||
cd "$ORG_OUTPUT_DIR"
|
||||
|
||||
echo "HTML published to: $ORG_OUTPUT_DIR"
|
||||
|
||||
# Git commands
|
||||
echo "Adding changes to Git..."
|
||||
git add .
|
||||
|
||||
echo "Committing..."
|
||||
git commit -m "Auto-publish on $(date)" || echo "Nothing to commit."
|
||||
|
||||
echo "Pushing to GitHub..."
|
||||
git push origin main
|
||||
|
||||
echo "Done! Changes pushed and Cloudflare should rebuild the site."
|
||||
|
||||
#+END_SRC
|
||||
|
||||
[fn:1] See: [[~/master-folder/org_files/org_web/blogs/publish-pages.org][this link]]
|
||||
19
backups/solid-principles.org~
Normal file
19
backups/solid-principles.org~
Normal file
@@ -0,0 +1,19 @@
|
||||
#+TITLE: SOLID Principles
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-10-18 Sat 19:14>
|
||||
#+FILETAGS: :learning:notes:
|
||||
|
||||
The SOLID principles are five design guidelines for writing maintainable, flexible, and scalable object-oriented code. Here's a quick summary:
|
||||
|
||||
1. Single Responsibility Principle (SRP): A class should have only one reason to change, meaning it should have a single responsibility or purpose.
|
||||
|
||||
2. Open/Closed Principle (OCP): Classes should be open for extension (e.g., adding new functionality) but closed for modification (no need to change existing code).
|
||||
|
||||
3. Liskov Substitution Principle (LSP): Subclasses should be substitutable for their base classes without breaking the program's behavior.
|
||||
|
||||
4. Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they don't use, favoring specific, small interfaces over large, general ones.
|
||||
|
||||
5. Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules; both should depend on abstractions (e.g., interfaces), and abstractions should not depend on details.
|
||||
|
||||
These principles promote cleaner, more modular, and testable code.
|
||||
|
||||
36
backups/spending-the-whole-day-on-this-website.org~
Normal file
36
backups/spending-the-whole-day-on-this-website.org~
Normal file
@@ -0,0 +1,36 @@
|
||||
#+TITLE: 09-08-2025: Website Changes
|
||||
#+OPTIONS: num:nil toc:nil
|
||||
#+DATE: <2025-08-09 Sat>
|
||||
#+FILETAGS: :emacs:website:
|
||||
|
||||
* What was worked on
|
||||
|
||||
I saw a few websites that used marginal notes on the right side of the screen like [[https://ogbe.net/blog/emacs_org_static_site][this one]], so I decided to try and implement this feature in this wesbite. In my mind I thought of using an external pre-configured CSS that I can just insert and ta-da it would work.
|
||||
|
||||
How wrong I was.
|
||||
|
||||
See, this website is published with ~org-publish~, meaning the org files that are written are converted to HTML through a transcoder. And as I already had a ~org.css~[fn:1] stylesheet which was primarily used for syntax highlighting, it caused some layout and formatting issue when I tried to use something like ~tufte.css~[fn:2]. Anyways, long story short, the main thing I worked on today was styling this website, and ensuring certain macros worked:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
||||
(setq org-export-global-macros
|
||||
(append
|
||||
'(("sidenote"
|
||||
. "@@html:<label for=\"sn$1\" class=\"margin-toggle sidenote-number\"></label><input type=\"checkbox\" id=\"sn$1\" class=\"margin-toggle\"/><span class=\"sidenote\">$2</span>@@")
|
||||
("epigraph" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2</footer></blockquote></div>@@")
|
||||
("epigraph_single" . "@@html:<div class=\"epigraph\"><blockquote>$1</blockquote></div>@@")
|
||||
("epigraph3" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2, <cite>$3</cite></footer></blockquote></div>@@")
|
||||
("kbd" . "@@html:<kbd>$1</kbd>@@@@latex:\\texttt{$1}@@")
|
||||
("margimg"
|
||||
. "@@html:<aside class=\"marginnote\"><figure class=\"mn-fig\"><img src=\"$1\" alt=\"$2\" class=\"mn-img\" loading=\"lazy\" decoding=\"async\"/>$3</figure></aside>@@")
|
||||
|
||||
org-export-global-macros)))
|
||||
|
||||
#+END_SRC
|
||||
|
||||
This worked quite nicely, and I made sure to make these changes accessible on smaller devices. Anyways, I quite like this style of posting. Nothing formal nor technical, just a bunch of rambling on about random things.
|
||||
|
||||
Oh, and also there's a darkmode theme switch now for you night owls.
|
||||
|
||||
[fn:1] See: https://gongzhitaao.org/orgcss/
|
||||
[fn:2] See: https://edwardtufte.github.io/tufte-css/
|
||||
98
backups/status-board.js~
Normal file
98
backups/status-board.js~
Normal file
@@ -0,0 +1,98 @@
|
||||
const STATES = [
|
||||
{ key: "completed", label: "Completed" },
|
||||
{ key: "manager_review", label: "Manager Review" },
|
||||
{ key: "in_progress", label: "In Progress" },
|
||||
{ key: "not_started", label: "Not Started" },
|
||||
{ key: "comments", label: "Comments" }
|
||||
];
|
||||
|
||||
let draggedItemId = null;
|
||||
|
||||
function countByState(items) {
|
||||
return items.reduce((acc, item) => {
|
||||
acc[item.state] = (acc[item.state] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "kanban-card";
|
||||
card.draggable = true;
|
||||
card.dataset.id = item.id;
|
||||
card.textContent = item.title;
|
||||
|
||||
card.addEventListener("dragstart", () => {
|
||||
draggedItemId = item.id;
|
||||
card.classList.add("dragging");
|
||||
});
|
||||
|
||||
card.addEventListener("dragend", () => {
|
||||
draggedItemId = null;
|
||||
card.classList.remove("dragging");
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createColumn(state, items, counts) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "kanban-column";
|
||||
col.dataset.state = state.key;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerHTML = `
|
||||
<span class="kanban-title">${state.label}</span>
|
||||
<span class="kanban-count">${counts[state.key] || 0}</span>
|
||||
`;
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "kanban-list";
|
||||
|
||||
list.addEventListener("dragover", e => e.preventDefault());
|
||||
|
||||
list.addEventListener("drop", async () => {
|
||||
if (!draggedItemId) return;
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${draggedItemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: state.key })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard(); // simple & safe re-render
|
||||
} else {
|
||||
alert("Failed to update state");
|
||||
}
|
||||
});
|
||||
|
||||
items
|
||||
.filter(i => i.state === state.key)
|
||||
.forEach(i => list.appendChild(createCard(i)));
|
||||
|
||||
col.appendChild(header);
|
||||
col.appendChild(list);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
function renderBoard(items) {
|
||||
const board = document.getElementById("kanban-board");
|
||||
board.innerHTML = "";
|
||||
|
||||
const counts = countByState(items);
|
||||
|
||||
STATES.forEach(state => {
|
||||
board.appendChild(createColumn(state, items, counts));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function loadBoard() {
|
||||
const res = await fetch("/api/competencies/items");
|
||||
const items = await res.json();
|
||||
renderBoard(items);
|
||||
}
|
||||
|
||||
loadBoard();
|
||||
16
backups/status.org~
Normal file
16
backups/status.org~
Normal file
@@ -0,0 +1,16 @@
|
||||
#+TITLE: Competency Status Board
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+NO_SIDENOTES: t
|
||||
#+COMMENTS: t
|
||||
#+SLUG: competency-board
|
||||
|
||||
|
||||
This page shows the status of the competencies I am working on (*grad*)
|
||||
|
||||
#+BEGIN_EXPORT html
|
||||
<div id="kanban-board">
|
||||
<p>Loading status…</p>
|
||||
</div>
|
||||
|
||||
<script src="../assets/scripts/status-board.js"></script>
|
||||
#+END_EXPORT
|
||||
35
backups/syntax.org~
Normal file
35
backups/syntax.org~
Normal file
@@ -0,0 +1,35 @@
|
||||
Okay the syntax for these files might take some time to get used to. But here is a quick summary (message me in case anything doesn't make sense).
|
||||
|
||||
Headings / outlines are like: =* H1=, =** H2=, =*** H3=
|
||||
|
||||
So you can create a new header like:
|
||||
|
||||
*** Example
|
||||
|
||||
This is at level three (3 asterisks):
|
||||
|
||||
- Emphasis: =*bold*= =/italic/= =_underline_= =+strike+= =~code~= ==verbatim==
|
||||
|
||||
- Checkboxes: =- [ ] todo=, =- [X] done=, =- [-] partial=
|
||||
|
||||
- TODO workflow: =* TODO Write post= → DONE; define states: =#+TODO: TODO DOING | DONE=
|
||||
|
||||
- Timestamps: active =<2025-11-02 Sun 14:00>= ; inactive =[2025-11-02]=
|
||||
|
||||
- Links: =[[https://example.com][desc]]=
|
||||
|
||||
For a file tag, you can create a new file under the folder *lima*, make sure it has the ~.org~ extension. Then you simply can link it:
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
|
||||
[[file:file_name.org][Alias]]
|
||||
|
||||
#+end_src
|
||||
|
||||
It will look like this:
|
||||
|
||||
[[file:file_name.org][Alias]]
|
||||
|
||||
This should be a quick start (once you get used to it, I'll update it with macros :))
|
||||
|
||||
ADD AS MANY FILES AS YOU WANT (just don't forget to link them!)
|
||||
18
backups/third-time.org~
Normal file
18
backups/third-time.org~
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: Third Time
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-28 Thu 17:08>
|
||||
#+FILETAGS: :insights:
|
||||
|
||||
* A more structured way of working
|
||||
{{{sidenote(1, Useful for people who don't like the constrained working limit of 25 minutes for instance.)}}}
|
||||
|
||||
Whilst researching for different ways to use pomodoro [fn:1] on Emacs [fn:2], I came across this package titled: ~pomm.el~. In this package it had an interesting feature called /pomm-third-time/[fn:3]. The ideology behind is as follows:
|
||||
- You work as long as you want.
|
||||
- Your break is calculated using the formula: ~Time of break = 1/3 x Time of work~.
|
||||
- If you take a shorter break, the remaining break time is accumulated and added to the next break (within the same session).
|
||||
|
||||
[fn:1] - https://en.wikipedia.org/wiki/Pomodoro_Technique
|
||||
|
||||
[fn:2] - https://www.gnu.org/software/emacs/
|
||||
|
||||
[fn:3] - https://www.lesswrong.com/posts/RWu8eZqbwgB9zaerh/third-time-a-better-way-to-work
|
||||
18
backups/wacom-with-arch.org~
Normal file
18
backups/wacom-with-arch.org~
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: Wacom With Arch
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :insights:
|
||||
|
||||
* Setting up Wacom Tablet in Arch Hyprland
|
||||
|
||||
I purchased a [[https://www.wacom.com/en-gb/products/one-by-wacom][One by Wacom]] tablet almost 2 years ago now and it works flawlessly in both Windows and MacOS and even in most Linux distros with some configuration.
|
||||
|
||||
However with Arch and Hyprland, there were some issues related to the ~xserver~ it was running on. The X input driver module (e.g. xf86-input-wacom) is linked into the X server which in turn is in charge of rendering and generic input events. Hyprland on the other hand uses the Wayland display server protocol (replacement for the X protocol) hence why there is difficulty in input mapping.
|
||||
|
||||
I came across this issue as I was able to get the tablet to work across multiple monitors (mapping to a single one) through the ~xsetwacom~ function - which, at the time I was using Ubuntu. But with Hyprland there needs to be some manual intervention. I came across this [[https://thomaspurnell.com/003_wacom_graphics_hyprland/wacom_hyprland.html][post]] which explains nicely how to map the tablet to a single monitor. I took this approach with a slight configuration; in my ~hyprland.conf~ file, I simply added the following line:
|
||||
|
||||
#+BEGIN_SRC
|
||||
exec-once = hyprctl keyword "device[wacom-one-by-wacom-m-pen]:output" "DP-1"
|
||||
#+END_SRC
|
||||
|
||||
which mapped the device to the primary monitor DP-1.
|
||||
29
backups/what-do-i-want-to-do-with-emacs.org~
Normal file
29
backups/what-do-i-want-to-do-with-emacs.org~
Normal file
@@ -0,0 +1,29 @@
|
||||
#+TITLE: What Do I Want To Do With Emacs
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :emacs:review:
|
||||
|
||||
I first started using Emacs (and Linux for that matter) since around the end of 2023, and it's been well over a year of trial and error with this text editor. At first I was using this for all sorts of things such as:
|
||||
|
||||
- Dashboard
|
||||
- Agenda
|
||||
- Text editing
|
||||
- Magit (version controlling)
|
||||
- Syncthing Management
|
||||
|
||||
... and the list goes on.
|
||||
|
||||
One thing I realised is just how powerful Emacs really is, just recently I found out that you can use the ~emoji-insert~ to insert emojis! Some have even gone to say it's an operating system in its own right. So this prompted the question today, what do /I/ want from Emacs?
|
||||
|
||||
After some thinking I narrowed it down and collated it in the following diagram:
|
||||
|
||||
[[../../../assets/sketchnotes/2025-08-08-emacs.svg]]
|
||||
|
||||
Of course this is the current workflow, where the text in red are items I /currently/ use Emacs for (high percentage likelihood this will increase), and the items in blue text are ones I plan on adding to my workflow. The main thing I want from this is to ensure consistency in daily practices.
|
||||
|
||||
- Org-roam bodies a huge aspect of the daily workflow, as it is a second brain where all information is stored.
|
||||
- Learning elisp is primarily done in the scratch buffer and through projects.
|
||||
- Java/Python IDE for when I'm leetcoding or doing any arbitrary project.
|
||||
- Text editing mainly relates to config files for the arch setup.
|
||||
- Org-agenda (more on this [[file:org-agenda-usage.org][here]]).
|
||||
- Org-publish: see [[file:../../../setup.org][setup]].
|
||||
51
backups/wireframe-designs.org~
Normal file
51
backups/wireframe-designs.org~
Normal file
@@ -0,0 +1,51 @@
|
||||
#+TITLE: Wireframe Designs
|
||||
#+OPTIONS: num:nil tags:t toc:t
|
||||
#+DATE: <2025-11-06 Thu 22:01>
|
||||
#+FILETAGS: :learning:notes:
|
||||
#+WIP:
|
||||
|
||||
* What are wireframes?
|
||||
|
||||
Wireframes are basic visual representations of a website, app, or digital product that focus on structure and layout rather than design details like colors, fonts, or images. They typically include elements such as:
|
||||
|
||||
- Navigation menus
|
||||
|
||||
- Buttons
|
||||
|
||||
- Content blocks
|
||||
|
||||
- Headers and footers
|
||||
|
||||
Wireframes range from low-fidelity sketches to high-fidelity {{{sidenote(1, Low-fidelity (lo-fi) refers to a basic\, quick prototype that focuses on core concepts with minimal detail\, while high-fidelity (hi-fi) refers to a realistic\, detailed prototype that closely mimics the final product's look\, feel\, and functionality)}}} mockups, depending on the stage of the design process.
|
||||
|
||||
* Why wireframes are useful
|
||||
|
||||
Wireframes are essential tools in UX and UI design for several reasons:
|
||||
|
||||
- Clarify layout and functionality: They help designers and stakeholders visualise how users will interact with the product
|
||||
|
||||
- Facilitate collaboration: Wireframes serve as a communication tool between designers, developers, and clients, aligning everyone on structure and goals (close the feedback loop)
|
||||
|
||||
- Identify usability issues early: By focusing on user flow and interface logic, wireframes allow teams to spot and fix problems before investing in detailed design
|
||||
|
||||
- Support iterative design: Wireframes are easy to update and adapt, making them ideal for testing and refining ideas
|
||||
|
||||
* When to use wireframes?
|
||||
|
||||
Wireframes are typically created in the early stages of product development, before visual design and coding begin. They’re especially helpful during:
|
||||
|
||||
- Initial concept development
|
||||
|
||||
- Stakeholder presentations
|
||||
|
||||
- User testing and feedback sessions
|
||||
|
||||
* Competency example:
|
||||
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 112139.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 112206.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 112427.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 115621.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 115949.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 120038.png]]
|
||||
[[../../assets/images/wireframes/Screenshot 2025-10-31 120203.png]]
|
||||
21
backups/zettelkasten.org~
Normal file
21
backups/zettelkasten.org~
Normal file
@@ -0,0 +1,21 @@
|
||||
#+TITLE: Zettelkasten Method
|
||||
#+DATE: <2025-08-07 Thu>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :education:
|
||||
|
||||
* The Zettelkasten Method
|
||||
|
||||
This Zettelkasten method is growing in popularity for taking notes. Ever felt as though there's too much pieces of information but you don't know how to deal with it? This method solves exactly that problem and more. Think of it as forming a /second brain/, where you can jot down ideas and information whilst simultaneously being able to connect them together.
|
||||
|
||||
The [[https://zettelkasten.de/introduction/][official website]] describes in detail exactly what the Zettelkasten method is, providing use cases and benefits to this method. The part I wanted to highlighted in this is the following paragraph, where the author depicts how this concept would be explained to someone unfamiliar with it:
|
||||
|
||||
{{{epigraph_single(A Zettelkasten is a personal tool for thinking and writing. It has hypertextual features to make a web of thoughts possible. The difference to other systems is that you create a web of thoughts instead of notes of arbitrary size and form\, and emphasize connection\, not a collection.
|
||||
)}}}
|
||||
A Zettelkasten is a personal tool for thinking and writing. It has hypertextual features to make a web of thoughts possible. The difference to other systems is that you create a web of thoughts instead of notes of arbitrary size and form, and emphasize connection, not a collection.
|
||||
|
||||
|
||||
This web of thoughts is exactly what makes it useful, it takes on the seemingly cluttered /mess of information/ and is able to link the nodes together to create something meaningful and useful.
|
||||
|
||||
There are many personal knowledge management systems' (PKMS) out there that allow you to practice this method; the most common ones being [[https://obsidian.md/][Obsidian]] and [[https://www.orgroam.com/][Org Roam]]. After using Org Roam with Emacs for about a year or so, I realised how useful this method really is. A random idea can be jotted down and later connected to a bigger project or note system. The following picture below is a screenshot of a graph generated UI for all the nodes created in my Org Roam folder:
|
||||
|
||||
[[../../../assets/images/org-roam-ui-graph.png]]
|
||||
Reference in New Issue
Block a user