Skip to content

Commit ad04bb2

Browse files
committed
Updates title, subtitle, description, image formatting
1 parent 7b78fc2 commit ad04bb2

15 files changed

+242
-198
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Update Description
2+
3+
Based on the page content, generate a SEO-optimized meta description:
4+
- Length: 140-155 characters
5+
- Formula Structure: Problem/Benefit + What's Inside + Soft CTA
6+
- No formatting/markup - plain text only
7+
- Primary keyword early (first 60 characters for search bolding)
8+
- Use action verbs: Learn, Discover, Master, Build, etc.
9+
- Benefit-focused with specific details
10+
- Match content accurately
11+
- Emotional triggers: essential, proven, expert, comprehensive, etc.
12+
- Compelling: Appeals to the target audience

.cursor/commands/seo-images.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Update Image Formatting
2+
3+
For each image in the page, update the image formatting to be SEO-optimized:
4+
- Add all the image tags to the page for SEO and optimize them for SEO (src, alt, title, width and height to auto)
5+
- Optimize the image descriptions for SEO (alt, title, width, height)
6+
- Use formatting like here:
7+
```html
8+
loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;"
9+
```
10+
- If the name of the image is not descriptive, update the name of the image to be descriptive

.cursor/commands/seo-title.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Update Title
2+
3+
Based on the page content, generate a SEO-optimized title:
4+
- Should be under 100 characters
5+
- Focus primarily on the main themes and topics in the page content
6+
- Make it clear, keyword-rich, and engaging.

_posts/2020-11-29-segmentation.md

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
layout: post
3-
title: "Not A Regular RFM Analysis"
4-
subtitle: "Why limit to Recency, Frequency and Monetary measures during Customer Segmentation?"
5-
description: "Customer segmentation with limited data: learn a proven 5D RFM+ approach using k-means to segment responders/non-responders and drive targeted in-game marketing."
3+
title: "Customer Segmentation with RFM+ and K-Means: 7 Segments from Gaming Data"
4+
subtitle: "Build a 5D RFM+ framework, engineer metrics, and segment responders/non-responders with k-means to power targeted in‑game marketing"
5+
description: "Customer segmentation with limited data. Learn a 5D RFM+ framework, engineer metrics, and use k-means to create 7 segments—apply insights now."
66
image: "images/posts/2020-11-29-segmentation/cover.jpg"
77
authors: [nishantmohan]
88
tags: [analytics, clustering]
@@ -35,7 +35,10 @@ So let's start, shall we!?
3535

3636
Let's take a quick look at the available features.
3737

38-
<img src="/images/posts/2020-11-29-segmentation/data.jpg" />
38+
<figure>
39+
<img src="/images/posts/2020-11-29-segmentation/data.jpg" alt="Sample of gaming user-level dataset with purchase dates for base game, expansion packs, and downloadable content" title="User-Level Gaming Dataset Features" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
40+
<figcaption><p>Snapshot of available features used for segmentation: base game, expansions, and DLC install dates</p></figcaption>
41+
</figure>
3942

4043
So the last 8 features are the names of either an expansion pack of the game or a downloadable content. The dataset has 500k rows. That's good because it means we can make more segments, right!?
4144

@@ -53,7 +56,10 @@ I tag the users as responders or non-responders based on whether they buy any ad
5356

5457
Now I can begin defining my key metrics for segmenting the responders:
5558

56-
<img src="/images/posts/2020-11-29-segmentation/recency.jpg" />
59+
<figure>
60+
<img src="/images/posts/2020-11-29-segmentation/recency.jpg" alt="Recency distribution showing user activity recency across years with higher activity in 2019" title="Recency Distribution of Player Activity" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
61+
<figcaption><p>Recency metric: days since last activity, highlighting more recent engagement in 2019</p></figcaption>
62+
</figure>
5763

5864

5965
### Recency
@@ -62,7 +68,10 @@ This is the number of days passed since the user was seen active on the gaming p
6268

6369
The chart shows that more users have been active in 2019, as compared to the users in 2017.
6470

65-
<img src="/images/posts/2020-11-29-segmentation/frequency.jpg" />
71+
<figure>
72+
<img src="/images/posts/2020-11-29-segmentation/frequency.jpg" alt="Frequency distribution of days played since installation, skewed toward fewer active days" title="Frequency of Gameplay Days" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
73+
<figcaption><p>Frequency metric: number of active days since install, skewed toward fewer days for most players</p></figcaption>
74+
</figure>
6675

6776
### Frequency
6877

@@ -71,7 +80,10 @@ Since the day a player installed the game, how many days did he play the game?
7180
The chart is concentrated towards left, meaning that most players are active for lesser days. However, it should be noted that new players have less number of days where they could be active, as compared to older players.
7281

7382

74-
<img src="/images/posts/2020-11-29-segmentation/monetary-value.png" />
83+
<figure>
84+
<img src="/images/posts/2020-11-29-segmentation/monetary-value.png" alt="Monetary value distribution of player spending based on mapped add-on prices" title="Monetary Value of Player Spend" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
85+
<figcaption><p>Monetary value metric: spend estimated by mapping store prices to user add-on purchases</p></figcaption>
86+
</figure>
7587

7688

7789
### Monetary Value
@@ -80,7 +92,10 @@ Since this information is not available in the data, I went to the game store we
8092

8193
Most players spend less than a hundred bucks. This is expected because the base game costs 55 bucks. And the downloadable content is generally cheap!
8294

83-
<img src="/images/posts/2020-11-29-segmentation/responses.png" />
95+
<figure>
96+
<img src="/images/posts/2020-11-29-segmentation/responses.png" alt="Distribution of number of add-ons purchased per player showing most buyers purchase one" title="Responses: Add-ons Purchased per Player" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
97+
<figcaption><p>Responses metric: count of prior add-on purchases per player; most buyers purchase only one</p></figcaption>
98+
</figure>
8499

85100

86101
### Responses
@@ -89,7 +104,10 @@ How many add-ons did the player buy previously? This will not be correlated with
89104

90105
It can be seen that most people who bought any add-on, only bought one.
91106

92-
<img src="/images/posts/2020-11-29-segmentation/purchase-frequency.png" />
107+
<figure>
108+
<img src="/images/posts/2020-11-29-segmentation/purchase-frequency.png" alt="Histogram of purchase intervals showing peaks near expansion launch windows" title="Purchase Frequency Over Time" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
109+
<figcaption><p>Purchase frequency metric: intervals between purchases with peaks around expansion release periods</p></figcaption>
110+
</figure>
93111

94112

95113
### Purchase Frequency
@@ -104,7 +122,10 @@ While most players buy everything soon after they buy the game, we see other hig
104122

105123
Using the 5 key metrics, I apply k-means clustering to segment the users.
106124

107-
<img src="/images/posts/2020-11-29-segmentation/elbow.jpg" />
125+
<figure>
126+
<img src="/images/posts/2020-11-29-segmentation/elbow.jpg" alt="Elbow method chart indicating optimal k around five clusters for k-means" title="Elbow Method for Optimal k" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
127+
<figcaption><p>Elbow plot suggests k=5 as a balanced choice for k-means clustering complexity and cohesion</p></figcaption>
128+
</figure>
108129

109130
Looking at the chart, I select 5 as the optimum number of clusters/segments. This gives me a balance between homogeneity within clusters and complexity of the analysis.
110131

@@ -113,23 +134,32 @@ Looking at the chart, I select 5 as the optimum number of clusters/segments. Thi
113134

114135
Since these are the users who have not interacted much, we only have two measures to judge them: Recency and Frequency.
115136

116-
<img src="/images/posts/2020-11-29-segmentation/recency-vs-frequency.jpg" />
137+
<figure>
138+
<img src="/images/posts/2020-11-29-segmentation/recency-vs-frequency.jpg" alt="Scatter plot of recency versus frequency used to segment non-responders by activity threshold" title="Recency vs Frequency for Non-Responders" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
139+
<figcaption><p>Non-responder segmentation using a recency threshold to separate recently active from lapsed users</p></figcaption>
140+
</figure>
117141

118142
As can be seen in the above chart, I segment such users by a threshold of 1000 days. That is, those who have been active in last 200 days are in Cluster 6, others are in Cluster 5 (Cluster 0–4 being the responders).
119143

120144
## Analysis and Strategy
121145

122146
Following table gives means of all the features across the user segments.
123147

124-
<img src="/images/posts/2020-11-29-segmentation/segments.jpg" />
148+
<figure>
149+
<img src="/images/posts/2020-11-29-segmentation/segments.jpg" alt="Table of means for key metrics across identified customer segments" title="Segment Means Across Metrics" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
150+
<figcaption><p>Summary statistics by segment for recency, frequency, responses, monetary value, and purchase cadence</p></figcaption>
151+
</figure>
125152

126153
Look at the first row. On average, players in Cluster 0 were active for nearly 15 days, bought 1.5 add-ons, were active 477 days from the beginning (long back), spent 65 bucks, and purchased an add-on every 33 days. Since these were active long back, they have probably forgotten about the game. So, in-game marketing may not work on them! On the other hand, email marketing might!
127154

128155
Now look at the second row. On average, players in Cluster 1 were active for a whopping 92 days, bought nearly 3 add-ons, were active fairly recently, have spent much more than others have, but purchase relatively rarely. These could be the players who have recently bought an add-on. These are the customers who seem to be loyal. We could target them with more exciting features!
129156

130157
Following figure gives similar summary of each cluster/segment.
131158

132-
<img src="/images/posts/2020-11-29-segmentation/strategy.jpg" />
159+
<figure>
160+
<img src="/images/posts/2020-11-29-segmentation/strategy.jpg" alt="Per-segment strategy summary visualization guiding targeted marketing actions" title="Per-Segment Strategy Overview" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
161+
<figcaption><p>Actionable strategy guidance for each segment to tailor in-game and email marketing</p></figcaption>
162+
</figure>
133163

134164
## Conclusion
135165

_posts/2020-12-07-practical-guide-better-code.md

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
layout: post
3-
title: "A practical guide for better-looking python code"
4-
description: "Setting up a CI/CD pipeline using GitHub"
3+
title: "Python CI/CD with GitHub Actions: Pre-commit, Linters, and Pytest Guide"
4+
subtitle: "Step-by-step workflow to secure branches, automate linting, and run tests using GitHub Actions, pre-commit, black/isort/flake8/mypy, and pytest."
5+
description: "Python CI/CD with GitHub Actions: Discover branch protection, pre-commit, black, isort, flake8, mypy, and pytest to enforce essential, tested code—start now."
56
image: "images/posts/2020-12-07-practical-guide-better-code/cover.jpg"
67
authors: [olegpolivin]
78
tags: [github, python, cicd]
@@ -34,7 +35,10 @@ I create an empty repository to illustrate how one sets up a CI/CD pipeline step
3435
git clone https://github.com/olegpolivin/Fizz-Buzz-CI-CD.git
3536
```
3637

37-
<img src="/images/posts/2020-12-07-practical-guide-better-code/empty-repo.png" />
38+
<figure>
39+
<img src="/images/posts/2020-12-07-practical-guide-better-code/empty-repo.png" alt="New GitHub repository with only README on main branch" title="Empty Repository on GitHub" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
40+
<figcaption><p>Starting point: an empty repo with a single README on main</p></figcaption>
41+
</figure>
3842

3943

4044
### Rules for branches
@@ -43,7 +47,10 @@ git clone https://github.com/olegpolivin/Fizz-Buzz-CI-CD.git
4347
As usual I can work on the code, and then push to the `main` branch. That’s what I want to prohibit.
4448
Go to the `Settings` menu for a given repo and choose `Branches`.
4549

46-
<img src="/images/posts/2020-12-07-practical-guide-better-code/branches.png" />
50+
<figure>
51+
<img src="/images/posts/2020-12-07-practical-guide-better-code/branches.png" alt="GitHub settings page showing Branches section for adding protection rules" title="GitHub Branches Settings" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
52+
<figcaption><p>Navigate to Settings → Branches to configure protection rules</p></figcaption>
53+
</figure>
4754

4855

4956
There are two ways to prevent pushing to the main branch, and you can choose it in the Add rule section. They are:
@@ -58,7 +65,10 @@ However, indeed, this will prevent you from pushing to `main` branch, but you ca
5865

5966
Click on `Add rule`, and here is the rule that I’ve added:
6067

61-
<img src="/images/posts/2020-12-07-practical-guide-better-code/branch-protection.png" />
68+
<figure>
69+
<img src="/images/posts/2020-12-07-practical-guide-better-code/branch-protection.png" alt="Add branch protection rule modal with required status checks and include administrators" title="Add Branch Protection Rule" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
70+
<figcaption><p>Add a protection rule for main with required status checks and admin inclusion</p></figcaption>
71+
</figure>
6272

6373

6474
In particular, I have added:
@@ -125,7 +135,10 @@ Creating a pull request will run the script above. Pull request will always pass
125135
126136
It is necessary just to add some modifications to the `Settings -> Branches -> Rules part`. See what’s new:
127137

128-
<img src="/images/posts/2020-12-07-practical-guide-better-code/branch-protection-rule.png" />
138+
<figure>
139+
<img src="/images/posts/2020-12-07-practical-guide-better-code/branch-protection-rule.png" alt="List of branch protection rules showing required check build (3.7)" title="Branch Protection Rule with Required Check" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
140+
<figcaption><p>Required check “build (3.7)” appears after configuring the workflow</p></figcaption>
141+
</figure>
129142

130143
Notice that `build (3.7)` has appeared among status checks. This corresponds to the name of the job (`build`) and python version `3.7`. I made a small modification to the `README.md` file, and let’s see if I can push it now to the main branch. Here is the error I get:
131144

@@ -151,13 +164,19 @@ git push origin dev
151164
A new branch called `dev` is created on the remote repository. What’s left is to create a pull request, and merge it to the `main` branch.
152165

153166

154-
<img src="/images/posts/2020-12-07-practical-guide-better-code/pull-request.png" />
167+
<figure>
168+
<img src="/images/posts/2020-12-07-practical-guide-better-code/pull-request.png" alt="GitHub pull request UI ready to merge after checks" title="Pull Request Flow on GitHub" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
169+
<figcaption><p>Create a PR from your feature branch to main to trigger checks</p></figcaption>
170+
</figure>
155171

156172

157173
It becomes possible to merge after all checks are run:
158174

159175

160-
<img src="/images/posts/2020-12-07-practical-guide-better-code/status-check-passed.png" />
176+
<figure>
177+
<img src="/images/posts/2020-12-07-practical-guide-better-code/status-check-passed.png" alt="GitHub PR showing all status checks have passed" title="Status Checks Passed" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
178+
<figcaption><p>All required checks pass—your PR is ready to merge</p></figcaption>
179+
</figure>
161180

162181

163182
We would like to introduce actions or tests to be performed, before the pull request is ready to be approved, so let’s provide code that will be actually checked. We will consider solving the `FizzBuzz` problem, see the next section.
@@ -264,7 +283,10 @@ jobs:
264283

265284
Let’s now try to push the solution above to the repository.
266285

267-
<img src="/images/posts/2020-12-07-practical-guide-better-code/fail.png" />
286+
<figure>
287+
<img src="/images/posts/2020-12-07-practical-guide-better-code/fail.png" alt="GitHub Actions CI job failing due to linter or formatter issues" title="CI Job Failing Example" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
288+
<figcaption><p>Example of a failing CI run—fix issues locally and push updates</p></figcaption>
289+
</figure>
268290

269291

270292
And we see that it fails on the first check. When it fails it does not proceed to the next steps, but it turns out that the code above for solving the `FizzBuzz` problem will fail on every check.
@@ -360,12 +382,18 @@ After the file is created in the repository, run `pre-commit install` to install
360382
Here is a small test: let’s change the neat `fizzbuzz.py` code to get back to the one that does not pass the checks and see what happens. Here is a part of the result: we see where it fails. Note that the pre-commit hook modifies files for some commands (like black or isort).
361383

362384

363-
<img src="/images/posts/2020-12-07-practical-guide-better-code/pre-commit.png" />
385+
<figure>
386+
<img src="/images/posts/2020-12-07-practical-guide-better-code/pre-commit.png" alt="Terminal output showing pre-commit hooks failing on formatting and linting" title="Pre-commit Hooks Catch Issues" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
387+
<figcaption><p>Pre-commit prevents bad commits by running formatters and linters before commit</p></figcaption>
388+
</figure>
364389

365390
Coming back to the neat version of the `fizzbuzz.py`, the pre-commit hook test is passed. That’s how it looks like in my case:
366391

367392

368-
<img src="/images/posts/2020-12-07-practical-guide-better-code/pre-commit-pass.png" />
393+
<figure>
394+
<img src="/images/posts/2020-12-07-practical-guide-better-code/pre-commit-pass.png" alt="Terminal output showing all pre-commit hooks passing" title="Pre-commit Hooks Passing" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
395+
<figcaption><p>All hooks pass—your changes are clean and consistent</p></figcaption>
396+
</figure>
369397

370398
Nice!
371399

@@ -425,7 +453,10 @@ Append the code below to the `ci.yml` file:
425453

426454
And here is the result:
427455

428-
<img src="/images/posts/2020-12-07-practical-guide-better-code/test-pass.png" />
456+
<figure>
457+
<img src="/images/posts/2020-12-07-practical-guide-better-code/test-pass.png" alt="GitHub Actions run with pytest tests passing" title="Pytest Suite Passing in CI" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
458+
<figcaption><p>Unit tests executed by pytest pass successfully in CI</p></figcaption>
459+
</figure>
429460

430461

431462
But that was the case when everything is ok. We are happy.
@@ -452,7 +483,10 @@ def fizz_buzz(num: int) -> str:
452483

453484
Great, let’s push and see that one test has failed:
454485

455-
<img src="/images/posts/2020-12-07-practical-guide-better-code/test-fail.png" />
486+
<figure>
487+
<img src="/images/posts/2020-12-07-practical-guide-better-code/test-fail.png" alt="GitHub Actions run showing a failing pytest test case" title="Pytest Failure Caught in CI" loading="lazy" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;" />
488+
<figcaption><p>Failing test demonstrates how CI guards against regressions before merge</p></figcaption>
489+
</figure>
456490

457491

458492
That is, by introducing unit tests into the CI/CD pipeline we were able to catch the problem before merging pull request into the `main` branch.

0 commit comments

Comments
 (0)