Merge pull request #1414 from pateljannat/seo-improvements

fix: seo improvements
This commit is contained in:
Jannat Patel
2025-04-08 21:09:54 +05:30
committed by GitHub
6 changed files with 190 additions and 43 deletions

View File

@@ -7,8 +7,27 @@ on:
branches: [ main ]
jobs:
commit-lint:
name: 'Semantic Commits'
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 200
- uses: actions/setup-node@v4
with:
node-version: 20
check-latest: true
- name: Check commit titles
run: |
npm install @commitlint/cli @commitlint/config-conventional
npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
linters:
name: Semantic Commits
name: Semgrep Rules
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
@@ -20,8 +39,17 @@ jobs:
with:
python-version: '3.10'
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install and Run Pre-commit
uses: pre-commit/action@v2.0.3
uses: pre-commit/action@v3.0.1
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules

View File

@@ -70,7 +70,7 @@ jobs:
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -79,7 +79,7 @@ jobs:
${{ runner.os }}-yarn-ui-
- name: Cache cypress binary
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress

26
commitlint.config.js Normal file
View File

@@ -0,0 +1,26 @@
module.exports = {
parserPreset: "conventional-changelog-conventionalcommits",
rules: {
"subject-empty": [2, "never"],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"type-enum": [
2,
"always",
[
"build",
"chore",
"ci",
"docs",
"feat",
"fix",
"perf",
"refactor",
"revert",
"style",
"test",
"deprecate", // deprecation decision
],
],
},
};

View File

@@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="{{ favicon or '/assets/lms/frontend/favicon.png' }}" />
<link rel="icon" href="{{ favicon }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Frappe Learning</title>
<title>{{ title }}</title>
<meta name="title" content="{{ meta.title }}" />
<meta name="image" content="{{ meta.image }}" />
<meta name="description" content="{{ meta.description }}" />
@@ -23,17 +23,6 @@
<p>
{{ meta.description }}
</p>
<p>
The content here is just for seo purposes. The actual content will be loaded in a few seconds.
</p>
<p>
Seo checks if a page has more than 300 words. So, here are some more words to make it more than 300 words.
Page descriptions are the HTML meta tags that provide a brief summary of a web page.
Search engines use meta descriptions to help identify the page's topic - they don't use them to rank the page, but they do use them to determine whether or not to display the page in search results.
Meta descriptions are important because they're often the first thing people see when they're deciding which search result to click on.
They're also important because they can help improve your click-through rate (CTR) from search results.
A good meta description can entice people to click on your page instead of someone else's.
</p>
<a href="{{ meta.link }}">Know More</a>
</div>
</div>
@@ -41,8 +30,6 @@
<div id="popovers"></div>
<script>
window.csrf_token = '{{ csrf_token }}'
window.setup_complete = '{{ setup_complete }}'
document.getElementById('seo-content').style.display = 'none';
</script>
<script type="module" src="/src/main.js"></script>

View File

@@ -36,7 +36,7 @@
<span v-else> Learning </span>
</div>
<div
v-if="userResource"
v-if="userResource.data"
class="mt-1 text-sm text-ink-gray-7 leading-none"
>
{{ convertToTitleCase(userResource.data?.full_name) }}

View File

@@ -10,25 +10,55 @@ no_cache = 1
def get_context():
app_path = frappe.form_dict.get("app_path")
favicon = (
frappe.db.get_single_value("Website Settings", "favicon")
or "/assets/lms/frontend/favicon.png"
)
title = frappe.db.get_single_value("Website Settings", "app_name") or "Frappe Learning"
context = frappe._dict()
if app_path:
context.meta = get_meta(app_path)
else:
context.meta = {}
csrf_token = frappe.sessions.get_csrf_token()
frappe.db.commit() # nosemgrep
context.csrf_token = csrf_token
context.setup_complete = cint(frappe.get_system_settings("setup_complete"))
context.meta = get_meta(app_path, title, favicon)
capture("active_site", "lms")
context.favicon = frappe.db.get_single_value("Website Settings", "favicon")
context.title = title
context.favicon = favicon
return context
def get_meta(app_path):
def get_meta(app_path, title, favicon):
meta = {}
if app_path:
meta = get_meta_from_document(app_path, favicon)
route_meta = frappe.get_all("Website Meta Tag", {"parent": app_path}, ["key", "value"])
if len(route_meta) > 0:
for row in route_meta:
if row.key == "title":
meta["title"] = row.value
elif row.key == "image":
meta["image"] = row.value
elif row.key == "description":
meta["description"] = f"{meta.get('description', '')} {row.value}"
elif row.key == "keywords":
meta["keywords"] = f"{meta.get('keywords', '')} {row.value}"
elif row.key == "link":
meta["link"] = row.value
if not meta:
meta = {
"title": title,
"image": favicon,
"description": "Easy to use Learning Management System",
}
return meta
def get_meta_from_document(app_path, favicon):
if app_path == "courses":
return {
"title": _("Course List"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"image": favicon,
"description": "This page lists all the courses published on our website",
"keywords": "All Courses, Courses, Learn",
"link": "/courses",
@@ -47,13 +77,18 @@ def get_meta(app_path):
course = frappe.db.get_value(
"LMS Course",
course_name,
["title", "image", "short_introduction", "tags"],
["title", "image", "description", "tags"],
as_dict=True,
)
if course.description:
soup = BeautifulSoup(course.description, "html.parser")
course.description = soup.get_text()
return {
"title": course.title,
"image": course.image,
"description": course.short_introduction,
"description": course.description,
"keywords": course.tags,
"link": f"/courses/{course_name}",
}
@@ -61,7 +96,7 @@ def get_meta(app_path):
if app_path == "batches":
return {
"title": _("Batches"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"image": favicon,
"description": "This page lists all the batches published on our website",
"keywords": "All Batches, Batches, Learn",
"link": "/batches",
@@ -71,13 +106,18 @@ def get_meta(app_path):
batch = frappe.db.get_value(
"LMS Batch",
batch_name,
["title", "meta_image", "description", "category", "medium"],
["title", "meta_image", "batch_details", "category", "medium"],
as_dict=True,
)
if batch.batch_details:
soup = BeautifulSoup(batch.batch_details, "html.parser")
batch.batch_details = soup.get_text()
return {
"title": batch.title,
"image": batch.meta_image,
"description": batch.description,
"description": batch.batch_details,
"keywords": f"{batch.category} {batch.medium}",
"link": f"/batches/details/{batch_name}",
}
@@ -87,7 +127,7 @@ def get_meta(app_path):
if "new/edit" in app_path:
return {
"title": _("New Batch"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"image": favicon,
"description": "Create a new batch",
"keywords": "New Batch, Create Batch",
"link": "/lms/batches/new/edit",
@@ -95,13 +135,18 @@ def get_meta(app_path):
batch = frappe.db.get_value(
"LMS Batch",
batch_name,
["title", "meta_image", "description", "category", "medium"],
["title", "meta_image", "batch_details", "category", "medium"],
as_dict=True,
)
if batch.batch_details:
soup = BeautifulSoup(batch.batch_details, "html.parser")
batch.batch_details = soup.get_text()
return {
"title": batch.title,
"image": batch.meta_image,
"description": batch.description,
"description": batch.batch_details,
"keywords": f"{batch.category} {batch.medium}",
"link": f"/batches/{batch_name}",
}
@@ -109,7 +154,7 @@ def get_meta(app_path):
if app_path == "job-openings":
return {
"title": _("Job Openings"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"image": favicon,
"description": "This page lists all the job openings published on our website",
"keywords": "Job Openings, Jobs, Vacancies",
"link": "/job-openings",
@@ -120,13 +165,13 @@ def get_meta(app_path):
job_opening = frappe.db.get_value(
"Job Opportunity",
job_opening_name,
["job_title", "company_logo", "company_name"],
["job_title", "company_logo", "description"],
as_dict=True,
)
return {
"title": job_opening.job_title,
"image": job_opening.company_logo,
"description": job_opening.company_name,
"description": job_opening.description,
"keywords": "Job Openings, Jobs, Vacancies",
"link": f"/job-openings/{job_opening_name}",
}
@@ -134,7 +179,7 @@ def get_meta(app_path):
if app_path == "statistics":
return {
"title": _("Statistics"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"image": favicon,
"description": "This page lists all the statistics of this platform",
"keywords": "Enrollment Count, Completion, Signups",
"link": "/statistics",
@@ -179,3 +224,64 @@ def get_meta(app_path):
"keywords": f"{badge.title}, {badge.description}",
"link": f"/badges/{badgeName}/{email}",
}
if app_path == "quizzes":
return {
"title": _("Quizzes"),
"image": favicon,
"description": _("Test your knowledge with interactive quizzes and more."),
"keywords": "Quizzes, interactive quizzes, online quizzes",
"link": "/quizzes",
}
if re.match(r"^quizzes/[^/]+$", app_path):
quiz_name = app_path.split("/")[1]
quiz = frappe.db.get_value(
"LMS Quiz",
quiz_name,
["title"],
as_dict=True,
)
if quiz:
return {
"title": quiz.title,
"image": favicon,
"description": "Test your knowledge with interactive quizzes.",
"keywords": quiz.title,
"link": f"/quizzes/{quiz_name}",
}
if app_path == "assignments":
return {
"title": _("Assignments"),
"image": favicon,
"description": _("Test your knowledge with interactive assignments and more."),
"keywords": "Assignments, interactive assignments, online assignments",
"link": "/assignments",
}
if re.match(r"^assignments/[^/]+$", app_path):
assignment_name = app_path.split("/")[1]
assignment = frappe.db.get_value(
"LMS Assignment",
assignment_name,
["title"],
as_dict=True,
)
if assignment:
return {
"title": assignment.title,
"image": favicon,
"description": "Test your knowledge with interactive assignments.",
"keywords": assignment.title,
"link": f"/assignments/{assignment_name}",
}
if app_path == "programs":
return {
"title": _("Programs"),
"image": favicon,
"description": "This page lists all the programs published on our website",
"keywords": "All Programs, Programs, Learn",
"link": "/programs",
}