Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb6dd3bd3 |
BIN
__pycache__/seed_data.cpython-311.pyc
Normal file
BIN
__pycache__/seed_data.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,22 @@
|
||||
from django.contrib import admin
|
||||
from .models import Level, Question, Option, Score
|
||||
|
||||
# Register your models here.
|
||||
class OptionInline(admin.TabularInline):
|
||||
model = Option
|
||||
extra = 4
|
||||
|
||||
class QuestionAdmin(admin.ModelAdmin):
|
||||
inlines = [OptionInline]
|
||||
list_display = ('text', 'level', 'points', 'time_limit')
|
||||
list_filter = ('level',)
|
||||
|
||||
class LevelAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'order')
|
||||
|
||||
class ScoreAdmin(admin.ModelAdmin):
|
||||
list_display = ('player_name', 'score', 'level', 'date_played')
|
||||
list_filter = ('level', 'date_played')
|
||||
|
||||
admin.site.register(Level, LevelAdmin)
|
||||
admin.site.register(Question, QuestionAdmin)
|
||||
admin.site.register(Score, ScoreAdmin)
|
||||
72
core/migrations/0001_initial.py
Normal file
72
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-20 16:10
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Level',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200, verbose_name='Մակարդակ')),
|
||||
('description', models.TextField(blank=True, verbose_name='Նկարագրություն')),
|
||||
('order', models.IntegerField(default=0, verbose_name='Հերթականություն')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Մակարդակ',
|
||||
'verbose_name_plural': 'Մակարդակներ',
|
||||
'ordering': ['order'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Question',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', models.TextField(verbose_name='Հարց')),
|
||||
('points', models.IntegerField(default=10, verbose_name='Միավորներ')),
|
||||
('time_limit', models.IntegerField(default=30, verbose_name='Ժամանակ (վայրկյան)')),
|
||||
('level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='core.level', verbose_name='Մակարդակ')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Հարց',
|
||||
'verbose_name_plural': 'Հարցեր',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Option',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', models.CharField(max_length=500, verbose_name='Տարբերակ')),
|
||||
('is_correct', models.BooleanField(default=False, verbose_name='Ճիշտ է')),
|
||||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='core.question', verbose_name='Հարց')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Տարբերակ',
|
||||
'verbose_name_plural': 'Տարբերակներ',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Score',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('player_name', models.CharField(max_length=100, verbose_name='Խաղացող')),
|
||||
('score', models.IntegerField(verbose_name='Միավոր')),
|
||||
('date_played', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Ամսաթիվ')),
|
||||
('level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.level', verbose_name='Մակարդակ')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Արդյունք',
|
||||
'verbose_name_plural': 'Արդյունքներ',
|
||||
'ordering': ['-score', '-date_played'],
|
||||
},
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -1,3 +1,54 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
# Create your models here.
|
||||
class Level(models.Model):
|
||||
title = models.CharField(max_length=200, verbose_name="Մակարդակ")
|
||||
description = models.TextField(blank=True, verbose_name="Նկարագրություն")
|
||||
order = models.IntegerField(default=0, verbose_name="Հերթականություն")
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Մակարդակ"
|
||||
verbose_name_plural = "Մակարդակներ"
|
||||
ordering = ['order']
|
||||
|
||||
class Question(models.Model):
|
||||
level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="questions", verbose_name="Մակարդակ")
|
||||
text = models.TextField(verbose_name="Հարց")
|
||||
points = models.IntegerField(default=10, verbose_name="Միավորներ")
|
||||
time_limit = models.IntegerField(default=30, verbose_name="Ժամանակ (վայրկյան)")
|
||||
|
||||
def __str__(self):
|
||||
return self.text[:50]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Հարց"
|
||||
verbose_name_plural = "Հարցեր"
|
||||
|
||||
class Option(models.Model):
|
||||
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="options", verbose_name="Հարց")
|
||||
text = models.CharField(max_length=500, verbose_name="Տարբերակ")
|
||||
is_correct = models.BooleanField(default=False, verbose_name="Ճիշտ է")
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Տարբերակ"
|
||||
verbose_name_plural = "Տարբերակներ"
|
||||
|
||||
class Score(models.Model):
|
||||
player_name = models.CharField(max_length=100, verbose_name="Խաղացող")
|
||||
score = models.IntegerField(verbose_name="Միավոր")
|
||||
level = models.ForeignKey(Level, on_delete=models.CASCADE, verbose_name="Մակարդակ")
|
||||
date_played = models.DateTimeField(default=timezone.now, verbose_name="Ամսաթիվ")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.player_name} - {self.score}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Արդյունք"
|
||||
verbose_name_plural = "Արդյունքներ"
|
||||
ordering = ['-score', '-date_played']
|
||||
@ -1,25 +1,122 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<html lang="hy">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<meta property="og:description" content="{{ project_description }}">
|
||||
<meta property="twitter:description" content="{{ project_description }}">
|
||||
{% endif %}
|
||||
{% if project_image_url %}
|
||||
<meta property="og:image" content="{{ project_image_url }}">
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title|default:"Իմ հայրենիք" }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Armenian:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--am-red: #D90012;
|
||||
--am-blue: #0033A0;
|
||||
--am-orange: #F2A800;
|
||||
--dark-bg: #1a1a1a;
|
||||
--glass: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
font-family: 'Noto Sans Armenian', sans-serif;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
color: #333;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.9) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 3px solid var(--am-blue);
|
||||
}
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
color: var(--am-blue) !important;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: var(--am-blue);
|
||||
border-color: var(--am-blue);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #00267a;
|
||||
border-color: #00267a;
|
||||
}
|
||||
.btn-accent {
|
||||
background-color: var(--am-orange);
|
||||
color: white;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.btn-accent:hover {
|
||||
background-color: #d99600;
|
||||
color: white;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.hero-section {
|
||||
background: linear-gradient(rgba(0, 51, 160, 0.8), rgba(217, 0, 18, 0.8)), url('https://images.unsplash.com/photo-1579618210881-266895317796?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: white;
|
||||
padding: 100px 0;
|
||||
text-align: center;
|
||||
border-bottom: 5px solid var(--am-orange);
|
||||
}
|
||||
footer {
|
||||
margin-top: auto;
|
||||
background: #222;
|
||||
color: #ccc;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
<nav class="navbar navbar-expand-lg sticky-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{% url 'index' %}">Իմ հայրենիք</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'index' %}">Գլխավոր</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'leaderboard' %}">Լավագույնները</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/">Ադմին</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</html>
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2024 «Իմ հայրենիք» ինտելեկտուալ խաղ</p>
|
||||
<div style="display: flex; justify-content: center; gap: 5px; height: 10px;">
|
||||
<div style="width: 20px; background: var(--am-red);"></div>
|
||||
<div style="width: 20px; background: var(--am-blue);"></div>
|
||||
<div style="width: 20px; background: var(--am-orange);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@ -1,145 +1,67 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<header class="hero-section">
|
||||
<div class="container">
|
||||
<h1 class="display-3 fw-bold mb-4">Իմ Հայրենիք</h1>
|
||||
<p class="lead mb-5">Ինտելեկտուալ խաղ Հայաստանի, մշակույթի և պատմության մասին</p>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" id="player_name" class="form-control form-control-lg" placeholder="Մուտքագրեք ձեր անունը" aria-label="Nickname">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container py-5">
|
||||
<h2 class="text-center mb-5">Ընտրեք մակարդակը</h2>
|
||||
<div class="row g-4">
|
||||
{% for level in levels %}
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 text-center p-4">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title h4 mb-3">{{ level.title }}</h3>
|
||||
<p class="card-text text-muted mb-4">{{ level.description }}</p>
|
||||
<button onclick="startQuiz({{ level.id }})" class="btn btn-primary btn-lg w-100 px-5">Սկսել խաղը</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12 text-center">
|
||||
<p class="text-muted">Մակարդակներ դեռ չկան: Խնդրում ենք ավելացնել Ադմինի միջոցով:</p>
|
||||
<a href="/admin/core/level/add/" class="btn btn-outline-primary">Ավելացնել մակարդակ</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<section class="mt-5 py-5 bg-white rounded-4 shadow-sm text-center">
|
||||
<div class="row align-items-center px-4">
|
||||
<div class="col-md-6">
|
||||
<h2 class="fw-bold">Ինչո՞ւ խաղալ</h2>
|
||||
<ul class="list-unstyled text-start mt-4 d-inline-block">
|
||||
<li class="mb-2">✅ Սովորիր նոր փաստեր հայրենիքիդ մասին</li>
|
||||
<li class="mb-2">✅ Մրցիր ընկերներիդ հետ</li>
|
||||
<li class="mb-2">✅ Բարելավիր գիտելիքներդ</li>
|
||||
<li class="mb-2">✅ Զվարճացիր և հաղթիր</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img src="https://images.unsplash.com/photo-1541311029871-0f738e4a9e5b?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" alt="Armenia" class="img-fluid rounded-4">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function startQuiz(levelId) {
|
||||
const playerName = document.getElementById('player_name').value.trim() || 'Անանուն';
|
||||
localStorage.setItem('playerName', playerName);
|
||||
window.location.href = `/quiz/${levelId}/`;
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
46
core/templates/core/leaderboard.html
Normal file
46
core/templates/core/leaderboard.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<h1 class="text-center mb-5 fw-bold">Լավագույն 10 Խաղացողները</h1>
|
||||
|
||||
<div class="card shadow-lg p-4 border-0 rounded-4">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="py-3">#</th>
|
||||
<th class="py-3">Խաղացող</th>
|
||||
<th class="py-3">Միավոր</th>
|
||||
<th class="py-3">Մակարդակ</th>
|
||||
<th class="py-3">Ամսաթիվ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for score in top_scores %}
|
||||
<tr>
|
||||
<td class="fw-bold text-primary">{{ forloop.counter }}</td>
|
||||
<td class="fw-bold">{{ score.player_name }}</td>
|
||||
<td><span class="badge bg-success px-3 py-2 fs-6">{{ score.score }}</span></td>
|
||||
<td class="text-muted">{{ score.level.title }}</td>
|
||||
<td class="text-muted">{{ score.date_played|date:"d.m.Y" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-4 text-muted">Արդյունքներ դեռ չկան: Եղիր առաջինը!</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'index' %}" class="btn btn-primary btn-lg px-5 rounded-pill shadow">Խաղալ հիմա</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
216
core/templates/core/quiz.html
Normal file
216
core/templates/core/quiz.html
Normal file
@ -0,0 +1,216 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.quiz-container {
|
||||
max-width: 800px;
|
||||
margin: 50px auto;
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 15px 50px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress-bar {
|
||||
background-color: var(--am-blue);
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.timer-circle {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--am-orange);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 auto 30px;
|
||||
color: var(--am-orange);
|
||||
}
|
||||
.option-btn {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 12px;
|
||||
background: transparent;
|
||||
font-size: 1.1rem;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.option-btn:hover {
|
||||
border-color: var(--am-blue);
|
||||
background-color: rgba(0, 51, 160, 0.05);
|
||||
}
|
||||
.option-btn.correct {
|
||||
border-color: #28a745;
|
||||
background-color: rgba(40, 167, 69, 0.1);
|
||||
color: #28a745;
|
||||
}
|
||||
.option-btn.wrong {
|
||||
border-color: var(--am-red);
|
||||
background-color: rgba(217, 0, 18, 0.1);
|
||||
color: var(--am-red);
|
||||
}
|
||||
.score-badge {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--am-blue);
|
||||
}
|
||||
.result-view { display: none; text-align: center; }
|
||||
.quiz-view { display: block; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="quiz-container">
|
||||
<div id="progress" class="progress-bar" style="width: 0%"></div>
|
||||
|
||||
<!-- Quiz Play View -->
|
||||
<div id="quiz-view" class="quiz-view">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span id="question-count" class="text-muted">Հարց 1 / 10</span>
|
||||
<span class="score-badge">Միավորներ: <span id="current-score">0</span></span>
|
||||
</div>
|
||||
|
||||
<div class="timer-circle" id="timer">30</div>
|
||||
|
||||
<h2 id="question-text" class="text-center mb-5 h3 fw-bold">Բեռնում...</h2>
|
||||
|
||||
<div id="options-container" class="row">
|
||||
<!-- Options will be injected here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result View -->
|
||||
<div id="result-view" class="result-view">
|
||||
<h2 class="display-4 fw-bold mb-4">Խաղն ավարտվեց:</h2>
|
||||
<div class="my-5">
|
||||
<p class="h4 mb-2">Շնորհավորում ենք, <span id="final-player-name"></span>:</p>
|
||||
<p class="display-1 fw-bold text-primary mb-2" id="final-score">0</p>
|
||||
<p class="h4 text-muted">ընդհանուր միավոր</p>
|
||||
</div>
|
||||
<div class="d-grid gap-3 d-sm-flex justify-content-center">
|
||||
<a href="{% url 'index' %}" class="btn btn-outline-secondary btn-lg px-5">Նորից խաղալ</a>
|
||||
<a href="{% url 'leaderboard' %}" class="btn btn-accent btn-lg px-5">Տեսնել արդյունքները</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
const questions = {{ questions_json|safe }};
|
||||
const levelId = {{ level.id }};
|
||||
const playerName = localStorage.getItem('playerName') || 'Անանուն';
|
||||
|
||||
let currentQuestionIndex = 0;
|
||||
let score = 0;
|
||||
let timer;
|
||||
let timeLeft;
|
||||
|
||||
function initQuiz() {
|
||||
if (questions.length === 0) {
|
||||
document.getElementById('question-text').innerText = 'Այս մակարդակում հարցեր չկան:';
|
||||
return;
|
||||
}
|
||||
showQuestion();
|
||||
}
|
||||
|
||||
function showQuestion() {
|
||||
const question = questions[currentQuestionIndex];
|
||||
document.getElementById('question-text').innerText = question.text;
|
||||
document.getElementById('question-count').innerText = `Հարց ${currentQuestionIndex + 1} / ${questions.length}`;
|
||||
document.getElementById('progress').style.width = `${((currentQuestionIndex + 1) / questions.length) * 100}%`;
|
||||
|
||||
const optionsContainer = document.getElementById('options-container');
|
||||
optionsContainer.innerHTML = '';
|
||||
|
||||
question.options.forEach(option => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'option-btn';
|
||||
btn.innerText = option.text;
|
||||
btn.onclick = () => selectOption(option, btn);
|
||||
optionsContainer.appendChild(btn);
|
||||
});
|
||||
|
||||
startTimer(question.time_limit);
|
||||
}
|
||||
|
||||
function startTimer(seconds) {
|
||||
clearInterval(timer);
|
||||
timeLeft = seconds;
|
||||
document.getElementById('timer').innerText = timeLeft;
|
||||
|
||||
timer = setInterval(() => {
|
||||
timeLeft--;
|
||||
document.getElementById('timer').innerText = timeLeft;
|
||||
if (timeLeft <= 0) {
|
||||
clearInterval(timer);
|
||||
nextQuestion();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function selectOption(option, btn) {
|
||||
clearInterval(timer);
|
||||
const options = document.querySelectorAll('.option-btn');
|
||||
options.forEach(opt => opt.disabled = true);
|
||||
|
||||
if (option.is_correct) {
|
||||
btn.classList.add('correct');
|
||||
score += questions[currentQuestionIndex].points;
|
||||
document.getElementById('current-score').innerText = score;
|
||||
} else {
|
||||
btn.classList.add('wrong');
|
||||
// Show the correct one
|
||||
options.forEach((opt, idx) => {
|
||||
if (questions[currentQuestionIndex].options[idx].is_correct) {
|
||||
opt.classList.add('correct');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(nextQuestion, 1500);
|
||||
}
|
||||
|
||||
function nextQuestion() {
|
||||
currentQuestionIndex++;
|
||||
if (currentQuestionIndex < questions.length) {
|
||||
showQuestion();
|
||||
} else {
|
||||
finishQuiz();
|
||||
}
|
||||
}
|
||||
|
||||
function finishQuiz() {
|
||||
document.getElementById('quiz-view').style.display = 'none';
|
||||
document.getElementById('result-view').style.display = 'block';
|
||||
document.getElementById('final-score').innerText = score;
|
||||
document.getElementById('final-player-name').innerText = playerName;
|
||||
|
||||
// Submit score to server
|
||||
fetch('/submit_score/', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
player_name: playerName,
|
||||
score: score,
|
||||
level_id: levelId
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = initQuiz;
|
||||
</script>
|
||||
{% endblock %}
|
||||
10
core/urls.py
10
core/urls.py
@ -1,7 +1,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import home
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
]
|
||||
path('', views.index, name='index'),
|
||||
path('quiz/<int:level_id>/', views.quiz_view, name='quiz'),
|
||||
path('submit_score/', views.submit_score, name='submit_score'),
|
||||
path('leaderboard/', views.leaderboard, name='leaderboard'),
|
||||
]
|
||||
@ -1,25 +1,70 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
now = timezone.now()
|
||||
import json
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from .models import Level, Question, Option, Score
|
||||
|
||||
def index(request):
|
||||
levels = Level.objects.all()
|
||||
context = {
|
||||
"project_name": "New Style",
|
||||
"agent_brand": agent_brand,
|
||||
"django_version": django_version(),
|
||||
"python_version": platform.python_version(),
|
||||
"current_time": now,
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
'levels': levels,
|
||||
'title': 'Իմ հայրենիք - Գլխավոր',
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
return render(request, 'core/index.html', context)
|
||||
|
||||
def quiz_view(request, level_id):
|
||||
level = get_object_or_404(Level, id=level_id)
|
||||
questions = level.questions.all()
|
||||
|
||||
# Shuffle or select specific questions if needed
|
||||
question_data = []
|
||||
for q in questions:
|
||||
options = []
|
||||
for opt in q.options.all():
|
||||
options.append({
|
||||
'id': opt.id,
|
||||
'text': opt.text,
|
||||
'is_correct': opt.is_correct
|
||||
})
|
||||
question_data.append({
|
||||
'id': q.id,
|
||||
'text': q.text,
|
||||
'points': q.points,
|
||||
'time_limit': q.time_limit,
|
||||
'options': options
|
||||
})
|
||||
|
||||
context = {
|
||||
'level': level,
|
||||
'questions_json': json.dumps(question_data),
|
||||
'title': f'Իմ հայրենիք - {level.title}',
|
||||
}
|
||||
return render(request, 'core/quiz.html', context)
|
||||
|
||||
@csrf_exempt
|
||||
def submit_score(request):
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
player_name = data.get('player_name', 'Անանուն')
|
||||
score_value = data.get('score', 0)
|
||||
level_id = data.get('level_id')
|
||||
|
||||
level = Level.objects.get(id=level_id)
|
||||
score_obj = Score.objects.create(
|
||||
player_name=player_name,
|
||||
score=score_value,
|
||||
level=level
|
||||
)
|
||||
return JsonResponse({'status': 'success', 'score_id': score_obj.id})
|
||||
except Exception as e:
|
||||
return JsonResponse({'status': 'error', 'message': str(e)}, status=400)
|
||||
return JsonResponse({'status': 'error', 'message': 'Invalid request method'}, status=405)
|
||||
|
||||
def leaderboard(request):
|
||||
top_scores = Score.objects.all()[:10]
|
||||
context = {
|
||||
'top_scores': top_scores,
|
||||
'title': 'Իմ հայրենիք - Լավագույնները',
|
||||
}
|
||||
return render(request, 'core/leaderboard.html', context)
|
||||
Loading…
x
Reference in New Issue
Block a user