Vienkopus sakopota uzziņu lapa: priekšgala (HTML, CSS, JavaScript), aizmugures (Python, PostgreSQL), izstrādes vide un drošības labās prakses.
<!DOCTYPE html>
<html lang="lv">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lapas virsraksts</title>
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>
<body>
<header class="topbar">
<a class="brand" href="index.html">ebSkola</a>
<nav>
<a href="index.html">Sākums</a>
<a href="projekts.html">Projekts</a>
</nav>
</header>
<main>
<section class="hero">
<p class="eyebrow">HTML · CSS · JavaScript</p>
<h1>Mana pirmā lapa</h1>
<p>Īss apraksts par projektu un to, ko lietotājs šeit var darīt.</p>
<button id="poga">Pārbaudīt</button>
</section>
<section class="card">
<h2>Projekta saturs</h2>
<p id="rezultats">Šeit parādīsies JavaScript rezultāts.</p>
</section>
</main>
<footer>© 2026 Mans projekts</footer>
</body>
</html>
style.css paraugs:root {
--bg: #222831;
--surface: #2A2F34;
--surface-raised: #393E46;
--text: #E7DEC9;
--muted: #C8BCA4;
--accent: #A89C88;
--accent-strong: #FFB142;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
.topbar,
main,
footer {
max-width: 960px;
margin: 0 auto;
padding: 24px;
}
.topbar {
display: flex;
justify-content: space-between;
gap: 16px;
border-bottom: 1px solid #4a515c;
}
.brand,
nav a {
color: var(--text);
text-decoration: none;
}
nav {
display: flex;
gap: 12px;
}
.hero,
.card {
background: var(--surface);
border: 1px solid #4a515c;
border-radius: 8px;
padding: 24px;
margin-top: 24px;
}
.eyebrow {
color: var(--muted);
font-size: 14px;
}
h1,
h2 {
margin-top: 0;
}
button {
background: var(--accent);
border: 0;
border-radius: 4px;
padding: 10px 14px;
cursor: pointer;
}
Lai mainītu izskatu, izmanto CSS īpašības <style> blokā vai style.css failā.
| Darbība | CSS īpašība | Piemērs |
|---|---|---|
| Fona krāsa | background-color | body { background-color: #f0f0f0; } |
| Teksta krāsa | color | p { color: #333333; } |
| Teksta centrēšana | text-align | h1 { text-align: center; } |
| Elementa centrēšana | margin: auto | div { width: 50%; margin: auto; } |
| Fonta veids | font-family | body { font-family: sans-serif; } |
| Teksta izmērs | font-size | p { font-size: 16px; } |
/* Iecentrē visu saturu lapas vidū */
.centrets-konteiners {
display: flex;
justify-content: center; /* Horizontāli */
align-items: center; /* Vertikāli */
height: 100vh; /* Pilns ekrāna augstums */
}
| Tags | Lietojums |
|---|---|
<div> | Universāls bloka konteiners. |
<span> | Universāls rindas (inline) konteiners. |
<p> | Teksta rindkopa. |
<a href="..."> | Hipersaite. |
<img src="..." alt="..."> | Attēls. |
<ul> / <ol> / <li> | Saraksti. |
<h1> - <h6> | Virsraksti. |
Semantiskie tagi pasaka pārlūkam un meklētājiem, kāda loma ir saturam. Tie arī palīdz ekrāna lasītājiem saprast lapas struktūru.
<main>
<article class="lesson-card">
<header>
<p class="eyebrow">Programmēšana I</p>
<h2>5.1 Saraksti</h2>
</header>
<p>Apgūsti sarakstu izveidi, indeksus un metodes.</p>
<footer>
<a href="prog1_51.html">Atvērt nodarbību</a>
</footer>
</article>
</main>
Atceries: lapā parasti ir viens galvenais <h1>, bet katrai satura sadaļai drīkst būt savs <h2> vai <h3>.
<form action="/saglabat" method="POST">
<label for="vards">Vārds:</label>
<input id="vards" name="vards" type="text" required>
<label for="epasts">E-pasts:</label>
<input id="epasts" name="epasts" type="email" required>
<button type="submit">Sūtīt</button>
</form>
<input type="number" min="0" max="100" step="1">
<input type="date">
<input type="checkbox" id="piekrītu" name="piekrītu">
<textarea name="komentars" rows="4"></textarea>
<select name="kurss" required>
<option value="">Izvēlies kursu</option>
<option value="prog1">Programmēšana I</option>
<option value="prog2">Programmēšana II</option>
</select>
alt tekstu attēliem.<label> (izmantojot for un id).<main>, <article>), lai palīdzētu ekrāna lasītājiem.<img src="diagramma.png"
alt="Plūsmas diagramma ar trim soļiem: ievade, apstrāde, izvade"
loading="lazy">
<nav aria-label="Galvenā navigācija">
<a href="index.html">Tēmas</a>
<a href="paligm.html" aria-current="page">Palīgmateriāli</a>
</nav>
<!-- Audio -->
<audio controls preload="auto" loop>
<source src="muzika.mp3" type="audio/mpeg">
<source src="muzika.ogg" type="audio/ogg">
Tavs pārlūks neatbalsta audio.
</audio>
<!-- Video -->
<video width="640" height="360" controls poster="preview.jpg">
<source src="video.mp4" type="video/mp4">
<track kind="subtitles" src="subs-lv.vtt" srclang="lv" label="Latviski">
</video>
<!-- Canvas (zīmēšana JS palīdzību) -->
<canvas id="game" width="800" height="600"></canvas>
<!-- SVG inline -->
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#FFB142"/>
</svg>
Formāti: audio - MP3 (universāls), OGG (atvērts kods); video - MP4 H.264 (universāls), WebM (mūsdienīgs).
/* Tags */ h1 { color: red; }
/* Klase */ .karte { padding: 20px; }
/* ID */ #logo { width: 80px; }
/* Apvienots */ h2.galvenais { font-size: 2em; }
/* Bērnu */ ul > li { list-style: square; }
/* Pēctecis */ article p { line-height: 1.6; }
/* Pseidoklases */ a:hover { color: blue; }
input:focus { outline: 2px solid #FFB142; }
li:nth-child(odd) { background: #eee; }
/* Atribūti */ a[href^="https"] { color: green; }
.kaste {
/* No iekšas uz āru: content → padding → border → margin */
width: 200px;
padding: 16px;
border: 2px solid #948979;
margin: 20px;
box-sizing: border-box; /* width iekļauj padding+border */
}
.absolute { position: absolute; top: 10px; left: 20px; }
.fixed { position: fixed; bottom: 0; right: 0; }
.sticky { position: sticky; top: 0; }
.konteineris {
display: flex;
flex-direction: row; /* row | column */
justify-content: center; /* main ass: flex-start | center | space-between */
align-items: center; /* cross ass: flex-start | center | stretch */
gap: 20px;
flex-wrap: wrap;
}
.elements {
flex: 1; /* aug, lai aizpildītu */
flex-basis: 200px;
}
.actions {
display: flex;
gap: 12px;
justify-content: flex-end;
align-items: center;
}
.btn {
border: 0;
border-radius: 6px;
padding: 10px 14px;
background: #FFB142;
color: #222831;
font-weight: 700;
cursor: pointer;
}
.card-row {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.card {
flex: 1 1 220px;
padding: 16px;
border: 1px solid #444;
border-radius: 6px;
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.layout {
display: grid;
grid-template-areas:
"header header header"
"side main main"
"footer footer footer";
grid-template-columns: 200px 1fr 1fr;
}
.header { grid-area: header; }
.dashboard {
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
min-height: 100vh;
}
.sidebar { border-right: 1px solid #444; }
.content { padding: 24px; }
@media (max-width: 800px) {
.dashboard { grid-template-columns: 1fr; }
.sidebar { border-right: 0; border-bottom: 1px solid #444; }
}
:root {
--bg: #222831;
--surface: #2A2F34;
--text: #E7DEC9;
--muted: #C8BCA4;
--accent-strong: #FFB142;
}
body { background: var(--bg); color: var(--text); }
@media (max-width: 768px) {
.grid { grid-template-columns: 1fr; }
body { font-size: 16px; }
}
.poga { background: #948979; transition: all 0.3s ease; }
.poga:hover {
background: #FFB142;
transform: translateY(-3px);
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.zime { animation: pulse 2s infinite; }
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
}
.form-grid {
display: grid;
gap: 12px;
max-width: 520px;
}
label { font-weight: 700; }
input, select, textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid #666;
border-radius: 6px;
background: #222831;
color: #DFD0B8;
}
input:focus, select:focus, textarea:focus {
outline: 2px solid #FFB142;
border-color: transparent;
}
/* Transforms - pārveidojumi */
.karte {
transform: translate(20px, 10px) rotate(15deg) scale(1.2);
transition: transform 0.3s ease;
}
.karte:hover { transform: scale(1.1) rotate(-3deg); }
/* 3D perspektīva */
.kub {
transform: perspective(800px) rotateY(45deg) rotateX(20deg);
transform-style: preserve-3d;
}
/* Filters - vizuālie efekti */
.attels {
filter: blur(2px) brightness(1.2) contrast(1.1) grayscale(50%);
}
.attels:hover { filter: none; }
/* Blend modes */
.banner {
background-blend-mode: multiply;
mix-blend-mode: overlay;
}
/* Clip-path - formas */
.trijsturis {
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}
let mainīgais = 5; // var mainīt
const konstante = "teksts"; // nav maināms
// Tipi: number, string, boolean, null, undefined, array, object
const masivs = [1, 2, 3];
const objekts = { vārds: "Anna", vecums: 12 };
typeof skaitlis; // "number"
Array.isArray(masivs); // true
function saskaiti(a, b) { return a + b; }
const reizini = (a, b) => a * b;
function sveiks(vārds = "viesi") {
return `Sveiks, ${vārds}!`;
}
const skaiti = [1, 2, 3];
const visi = [...skaiti, 4, 5]; // spread
function sum(...numbers) { // rest
return numbers.reduce((a, b) => a + b);
}
const skaiti = [1, 2, 3, 4, 5];
skaiti.map(x => x * 2); // [2,4,6,8,10]
skaiti.filter(x => x > 2); // [3,4,5]
skaiti.reduce((a, b) => a + b, 0); // 15
skaiti.find(x => x > 3); // 4
skaiti.some(x => x > 4); // true
skaiti.every(x => x > 0); // true
skaiti.includes(3); // true
skaiti.sort((a, b) => b - a); // dilstoši
const elem = document.getElementById("logo");
const visi = document.querySelectorAll(".karte");
const pirmais = document.querySelector(".karte");
elem.textContent = "Jauns teksts";
elem.classList.add("aktiivs");
elem.classList.toggle("paslēpts");
elem.style.color = "red";
elem.setAttribute("data-id", "42");
const jauns = document.createElement("div");
jauns.textContent = "Sveiks!";
document.body.appendChild(jauns);
jauns.remove();
const stundas = [
{ nosaukums: "Saraksti", fails: "prog1_51.html" },
{ nosaukums: "Vārdnīcas", fails: "prog1_52.html" },
{ nosaukums: "For cikli", fails: "prog1_53.html" }
];
const saraksts = document.querySelector("#stundas");
saraksts.innerHTML = "";
for (const stunda of stundas) {
const li = document.createElement("li");
const saite = document.createElement("a");
saite.href = stunda.fails;
saite.textContent = stunda.nosaukums;
li.appendChild(saite);
saraksts.appendChild(li);
}
document.getElementById("poga").addEventListener("click", (e) => {
console.log("Nospiests!", e.target);
});
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft") speletajs.x -= 5;
if (e.key === "Enter") sākt();
});
form.addEventListener("submit", (e) => {
e.preventDefault();
const dati = new FormData(form);
});
const forma = document.querySelector("#pieteikums");
const kluda = document.querySelector("#kluda");
forma.addEventListener("submit", (e) => {
e.preventDefault();
const dati = new FormData(forma);
const vards = dati.get("vards")?.trim();
const punkti = Number(dati.get("punkti"));
if (!vards || Number.isNaN(punkti) || punkti < 0) {
kluda.textContent = "Ievadi vārdu un punktus virs 0.";
return;
}
kluda.textContent = "";
console.log({ vards, punkti });
});
async function ielādēDatus() {
try {
const r = await fetch("https://api.example.com/dati");
if (!r.ok) throw new Error("Tīkla kļūda");
const dati = await r.json();
console.log(dati);
} catch (kļūda) { console.error(kļūda); }
}
fetch("/saglabat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vārds: "Anna", punkti: 95 })
});
localStorage.setItem("highscore", "120");
localStorage.setItem("speletajs", JSON.stringify({ vārds: "Anna" }));
const score = localStorage.getItem("highscore");
const sp = JSON.parse(localStorage.getItem("speletajs") || "{}");
localStorage.removeItem("highscore");
localStorage.clear();
// ES6 klase
class Varonis {
#hp; // privāts lauks (# prefix)
constructor(vards, hp = 100) {
this.vards = vards;
this.#hp = hp;
}
get hp() { return this.#hp; }
cizt(b) {
this.#hp = Math.max(0, this.#hp - b);
if (this.#hp === 0) console.log(`${this.vards} miris!`);
}
static fabrika(tips) {
if (tips === "bruninieks") return new Varonis("Bruņinieks", 150);
return new Varonis("Cilvēks", 100);
}
}
// Mantošana
class Burvis extends Varonis {
constructor(vards) {
super(vards, 80);
this.mana = 100;
}
burvojums(merkis) {
if (this.mana >= 10) { this.mana -= 10; merkis.cizt(25); }
}
}
// ES6 moduļi
// fails: varonis.js
export class Varonis { /* ... */ }
export const MAX_HP = 999;
export default class Game { /* ... */ }
// fails: main.js
import Game, { Varonis, MAX_HP } from './varonis.js';
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const speletajs = { x: 40, y: 40, atrums: 180 };
const taustini = new Set();
let ieprieksejais = performance.now();
document.addEventListener("keydown", e => taustini.add(e.key));
document.addEventListener("keyup", e => taustini.delete(e.key));
function update(dt) {
if (taustini.has("ArrowRight")) speletajs.x += speletajs.atrums * dt;
if (taustini.has("ArrowLeft")) speletajs.x -= speletajs.atrums * dt;
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFB142";
ctx.fillRect(speletajs.x, speletajs.y, 32, 32);
}
function loop(laiks) {
const dt = (laiks - ieprieksejais) / 1000;
ieprieksejais = laiks;
update(dt);
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
vards = "Anna"
vecums = 12
augstums = 1.65
ir_skoleens = True
str(42); int("10"); float("3.14") # konvertācija
10 // 3 # 3 (veselo)
10 % 3 # 1 (atlikums)
2 ** 8 # 256
f"Sveiks, {vards}! Vecums: {vecums}"
if punkti >= 90: print("Lieliski")
elif punkti >= 70: print("Labi")
else: print("Mēģini vēlreiz")
for i in range(10): print(i)
for v in saraksts: print(v)
for i, v in enumerate(saraksts): print(i, v)
while dzivibas > 0:
spelet()
# break, continue
for x in skaiti:
if x < 0: continue
if x > 100: break
def aprekini_vidu(skaiti):
"""Aprēķina vidējo vērtību."""
if not skaiti: return 0
return sum(skaiti) / len(skaiti)
reizina = lambda x, y: x * y
import random
from math import pi, sqrt
import json as j
saraksts = [1, 2, 3]
saraksts.append(4)
saraksts.insert(0, 0)
saraksts.remove(2)
saraksts.pop()
sorted(saraksts, reverse=True)
# List comprehension
kvadrati = [x**2 for x in range(10)]
parie = [x for x in saraksts if x % 2 == 0]
varonis = {"vards": "Anna", "hp": 100}
varonis["zelts"] = 50
varonis.get("inv", [])
for k, v in varonis.items(): ...
unikali = set([1, 1, 2, 3, 3]) # {1, 2, 3}
rezultati = [
{"vards": "Anna", "kurss": "Python", "punkti": 87},
{"vards": "Jānis", "kurss": "Python", "punkti": 72},
{"vards": "Eva", "kurss": "SQL", "punkti": 91},
]
pa_kursiem = {}
for rinda in rezultati:
kurss = rinda["kurss"]
pa_kursiem.setdefault(kurss, []).append(rinda["punkti"])
for kurss, punkti in pa_kursiem.items():
videjais = sum(punkti) / len(punkti)
print(kurss, round(videjais, 1))
with open("dati.txt", "r", encoding="utf-8") as f:
saturs = f.read()
with open("dati.txt", "w", encoding="utf-8") as f:
f.write("rinda 1\nrinda 2")
import json
with open("d.json") as f: data = json.load(f)
with open("d.json", "w") as f: json.dump(data, f, indent=2)
import csv
with open("d.csv") as f:
for r in csv.DictReader(f):
print(r["vards"])
try:
n = int(input("Ievadi: "))
except ValueError:
print("Tas nav skaitlis!")
finally:
print("Vienmēr izpildās")
import csv
lauki = ["vards", "punkti"]
rindas = [
{"vards": "Anna", "punkti": 120},
{"vards": "Jānis", "punkti": 95},
]
with open("rezultati.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=lauki)
writer.writeheader()
writer.writerows(rindas)
class Varonis:
def __init__(self, vards, hp=100):
self.vards = vards
self.hp = hp
def sanemt_bojajumu(self, bojajums):
self.hp -= bojajums
if self.hp <= 0:
print(f"{self.vards} ir miris!")
def __str__(self):
return f"{self.vards} ({self.hp} HP)"
class Burvis(Varonis):
def __init__(self, vards, mana=50):
super().__init__(vards, hp=80)
self.mana = mana
def burvojums(self, merkis):
if self.mana >= 10:
self.mana -= 10
merkis.sanemt_bojajumu(20)
from dataclasses import dataclass
@dataclass
class Prieksmets:
nosaukums: str
cena: int
retums: str = "parasts"
inventars = [
Prieksmets("Zobens", 40),
Prieksmets("Maģiska atslēga", 120, "rets"),
]
dargie = [p for p in inventars if p.cena >= 100]
# fails: kalkulators.py
def dalit(a, b):
if b == 0:
raise ValueError("Ar nulli dalīt nedrīkst")
return a / b
# fails: test_kalkulators.py
import pytest
from kalkulators import dalit
def test_dalit():
assert dalit(10, 2) == 5
def test_dalit_ar_nulli():
with pytest.raises(ValueError):
dalit(10, 0)
python -m pytest
python -m pytest -q
import functools
import time
# Vienkāršs dekorators
def izmeri_laiku(func):
@functools.wraps(func)
def ietinums(*args, **kwargs):
sakums = time.time()
rezultats = func(*args, **kwargs)
print(f"{func.__name__} aizņēma {time.time()-sakums:.2f}s")
return rezultats
return ietinums
@izmeri_laiku
def lens_aprekins():
time.sleep(1)
return 42
# Iebūvētie dekoratori
class Konts:
def __init__(self): self._bilance = 0
@property # getter
def bilance(self):
return self._bilance
@bilance.setter # setter
def bilance(self, v):
if v < 0: raise ValueError("Negatīvs!")
self._bilance = v
@staticmethod # nelieto self
def aprekini_procentus(suma, p): return suma * p / 100
@classmethod # cls
def tukss(cls): return cls()
# Context manager
class Faila_logger:
def __enter__(self):
self.f = open("log.txt", "a")
return self.f
def __exit__(self, *args):
self.f.close()
with Faila_logger() as f:
f.write("Notikums
")
CREATE DATABASE spele;
\c spele
CREATE TABLE speletaji (
id SERIAL PRIMARY KEY,
vards VARCHAR(50) NOT NULL UNIQUE,
epasts VARCHAR(120),
punkti INTEGER DEFAULT 0,
izveidots TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
DROP TABLE speletaji;
ALTER TABLE speletaji ADD COLUMN avatars TEXT;
ALTER TABLE speletaji DROP COLUMN epasts;
-- INSERT
INSERT INTO speletaji (vards, punkti) VALUES ('Anna', 120);
INSERT INTO speletaji (vards, punkti) VALUES
('Jānis', 95), ('Eva', 145);
-- SELECT
SELECT * FROM speletaji;
SELECT vards, punkti FROM speletaji WHERE punkti > 100;
SELECT * FROM speletaji ORDER BY punkti DESC LIMIT 10;
SELECT COUNT(*) FROM speletaji;
SELECT AVG(punkti), MAX(punkti) FROM speletaji;
-- UPDATE
UPDATE speletaji SET punkti = punkti + 10 WHERE vards = 'Anna';
-- DELETE
DELETE FROM speletaji WHERE punkti < 50;
| Tips | Apraksts |
|---|---|
SERIAL | Auto-increment vesels skaitlis |
INTEGER / BIGINT | Vesels skaitlis |
NUMERIC(10,2) | Decimālskaitlis |
VARCHAR(n) | Tekstam līdz n simboliem |
TEXT | Neierobežota teksta lauka |
BOOLEAN | true/false |
DATE / TIMESTAMP | Datums / datums + laiks |
JSON / JSONB | JSON dati |
UUID | Unikāls identifikators |
CREATE TABLE speles (
id SERIAL PRIMARY KEY,
speletajs_id INTEGER REFERENCES speletaji(id),
punkti INTEGER,
spelets TIMESTAMP DEFAULT NOW()
);
-- INNER JOIN
SELECT s.vards, sp.punkti
FROM speletaji s
INNER JOIN speles sp ON s.id = sp.speletajs_id;
-- LEFT JOIN
SELECT s.vards, COUNT(sp.id) AS speles_skaits
FROM speletaji s
LEFT JOIN speles sp ON s.id = sp.speletajs_id
GROUP BY s.vards;
CREATE TABLE rezultati (
id SERIAL PRIMARY KEY,
speletajs_id INTEGER NOT NULL REFERENCES speletaji(id) ON DELETE CASCADE,
punkti INTEGER NOT NULL CHECK (punkti >= 0),
limenis INTEGER NOT NULL DEFAULT 1,
UNIQUE (speletajs_id, limenis)
);
-- Transakcija: viss izdodas vai viss tiek atcelts
BEGIN;
UPDATE speletaji SET punkti = punkti + 10 WHERE id = 1;
INSERT INTO rezultati (speletajs_id, punkti, limenis) VALUES (1, 130, 2);
COMMIT;
-- Ja kļūda:
ROLLBACK;
-- UPSERT: ievieto vai atjaunina, ja tāds ieraksts jau eksistē
INSERT INTO rezultati (speletajs_id, punkti, limenis)
VALUES (1, 150, 2)
ON CONFLICT (speletajs_id, limenis)
DO UPDATE SET punkti = EXCLUDED.punkti;
CREATE INDEX idx_speletaji_vards ON speletaji(vards);
CREATE INDEX idx_punkti_dilst ON speletaji(punkti DESC);
CREATE UNIQUE INDEX idx_uniq_vards ON speletaji(vards);
EXPLAIN ANALYZE SELECT * FROM speletaji WHERE vards = 'Anna';
DROP INDEX idx_speletaji_vards;
import psycopg2
from psycopg2.extras import RealDictCursor
conn = psycopg2.connect(
host="localhost", database="spele",
user="lietotajs", password="parole"
)
with conn, conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute("SELECT * FROM speletaji WHERE punkti > %s", (50,))
for r in cur.fetchall():
print(r["vards"], r["punkti"])
cur.execute(
"INSERT INTO speletaji (vards, punkti) VALUES (%s, %s) RETURNING id",
("Anna", 120)
)
jauns_id = cur.fetchone()["id"]
# ⚠ NEKAD nelieto string formatēšanu SQL - SQL injekcijas risks!
import os
import psycopg2
conn = psycopg2.connect(os.environ["DATABASE_URL"])
with conn:
with conn.cursor() as cur:
cur.execute("SELECT NOW()")
print(cur.fetchone()[0])
-- Transakcija - visi vai neviens
BEGIN;
UPDATE konti SET bilance = bilance - 100 WHERE id = 1;
UPDATE konti SET bilance = bilance + 100 WHERE id = 2;
-- Ja kāds UPDATE neizdodas:
-- ROLLBACK;
COMMIT;
-- SAVEPOINT - daļēja atcelšana
BEGIN;
INSERT INTO logs VALUES (...);
SAVEPOINT pirms_riska;
UPDATE ... -- ja crashas
ROLLBACK TO pirms_riska; -- tikai šo daļu atceļ
COMMIT;
-- Constraints - datu integritāte
ALTER TABLE speletaji
ADD CONSTRAINT pozitivi_punkti CHECK (punkti >= 0);
ALTER TABLE speles
ADD CONSTRAINT fk_speletajs
FOREIGN KEY (speletajs_id)
REFERENCES speletaji(id)
ON DELETE CASCADE
ON UPDATE NO ACTION;
-- Triggers - automatiskas darbības
CREATE OR REPLACE FUNCTION pierakstit_izmainu()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO audit_log(tabula, darbiba, laiks)
VALUES (TG_TABLE_NAME, TG_OP, NOW());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER speletaja_izmaina
AFTER UPDATE OR DELETE ON speletaji
FOR EACH ROW EXECUTE FUNCTION pierakstit_izmainu();
git config --global user.name "Tavs Vārds"
git config --global user.email "tavs@pasts.lv"
git init
git clone https://github.com/lietotajs/repo.git
git status
git diff
git add fails.py # vai 'git add .'
git commit -m "Skaidrs apraksts"
git push
git pull
git log --oneline --graph --all
git show abc123
git branch
git branch jauna-funkcija
git checkout -b vel-jauns
git switch main
git merge jauna-funkcija
git branch -d jauna-funkcija
# Konflikta gadījumā: atrod <<<<<<< HEAD ... =======...>>>>>>>
# Izlemj versiju, git add, git commit
git switch main
git pull
git switch -c fix/seo-links
# veic izmaiņas
git status
git add paligm.html
git commit -m "Expand programming cheat sheet examples"
git push -u origin fix/seo-links
# GitHub: atver Pull Request, pārbaudi diff, sapludini main zarā
git restore fails.py # atceļ lokālas izmaiņas
git restore --staged fails.py # noņem no add
git reset --soft HEAD~1 # atceļ commit, izmaiņas paliek
git reset --hard HEAD~1 # ⚠ pilnībā atceļ
git revert abc123 # drošs commit, kas atceļ iepriekšējo
.env
*.key
secrets.json
__pycache__/
*.pyc
venv/
node_modules/
dist/
.DS_Store
.vscode/
.idea/
*.sqlite
# Stash - pagaidām paslēpj nesaglabātos darbus
git stash # paslēpj
git stash list # parāda visus
git stash pop # atjauno + dzēš no list
git stash apply stash@{1} # atjauno bez dzēšanas
git stash drop # tikai dzēš
# Cherry-pick - atsevišķu commit kopē uz citu branch
git checkout main
git cherry-pick abc123 # ievieto šo commit šeit
git cherry-pick abc123^..def456 # diapazons
# Interactive rebase - pārkārto/apvieno commits
git rebase -i HEAD~5
# Editorā: pick, reword (mainīt ziņu), squash (apvienot),
# fixup, edit, drop
# Reflog - atrod "pazudušus" commits
git reflog # visas darbības (pat dzēstas)
git reset --hard HEAD@{2} # atjauno 2 darbības atpakaļ
# Bisect - bināra meklēšana, kurš commit ievieš bug
git bisect start
git bisect bad # tagad ir slikti
git bisect good v1.0 # v1.0 bija labi
# Git dod commits to test → git bisect good/bad
git bisect reset # beigt
git tag v1.0.0
git tag -a v1.1.0 -m "Pirmā publiskā versija"
git push origin v1.1.0
git tag --list
# Pārslēgties uz konkrētu versiju tikai apskatei
git switch --detach v1.1.0
| Īsceļš | Darbība |
|---|---|
Ctrl+P | Atvērt failu pēc nosaukuma |
Ctrl+Shift+P | Komandu palete |
Ctrl+S | Saglabāt |
Ctrl+` | Atvērt termināli |
Ctrl+B | Sānjoslas slēptuve |
Ctrl+/ | Komentē rindu |
Alt+↑/↓ | Pārvietot rindu |
Ctrl+D | Atlasīt nākamo tādu pašu |
F2 | Pārdēvēt simbolu |
F12 | Iet uz definīciju |
Ctrl+Shift+F | Meklēt visā projektā |
| Īsceļš | Darbība |
|---|---|
Alt+Click | Pievieno kursoru tur, kur noklikšķini |
Ctrl+Alt+↑/↓ | Pievieno kursoru augstāk/zemāk |
Ctrl+D | Atlasi nākamo tādu pašu vārdu |
Ctrl+Shift+L | Atlasi VISUS tādus pašus vārdus |
Ctrl+Shift+Alt+↓ | Dublē rindu |
Shift+Alt+↑/↓ | Kopē rindu augšup/lejup |
// Pielāgots snippet (File → Preferences → Configure User Snippets → python.json)
{
"Print debug": {
"prefix": "pdb",
"body": [
"print(f"$1 = {$1}")"
],
"description": "Print mainīgo ar nosaukumu"
},
"For range": {
"prefix": "fr",
"body": [
"for ${1:i} in range(${2:10}):",
" $0"
]
}
}
Pēc pdb + Tab → izvēršas par print(f"x = {x}").
| Simbols | Nozīme |
|---|---|
. | Jebkurš simbols |
\d | Cipars |
\w | Vārda simbols |
\s | Atstarpe |
^ / $ | Rindas sākums / beigas |
* | 0+ reizes |
+ | 1+ reizes |
? | 0 vai 1 reizi |
{3,5} | No 3 līdz 5 reizēm |
[abc] | a, b vai c |
(grupa) | Grupēšana |
import re
re.match(r"^[\w.-]+@[\w.-]+\.\w{2,}$", "anna@skola.lv")
re.findall(r"\+?\d{8,12}", "Zvani: +37120123456")
re.sub(r"\d+", "###", "Skola Nr. 42")
teksts = "dat9_36.html - 3.6 Noslēguma projekts"
pattern = r"(?P<kurss>dat9)_(?P<tema>\d)(?P<stunda>\d)\.html"
m = re.search(pattern, teksts)
if m:
print(m.group("kurss")) # dat9
print(m.group("tema")) # 3
print(m.group("stunda")) # 6
# Vairāku atstarpju sakopšana
tirs = re.sub(r"\s+", " ", "Pārāk daudz atstarpju").strip()
| Sintakse | Nozīme |
|---|---|
(?=...) | Lookahead - sekos ... |
(?!...) | Negative lookahead - nesekos ... |
(?<=...) | Lookbehind - pirms ir ... |
(?<!...) | Negative lookbehind |
(?P<vards>...) | Nosaukta grupa |
(?:...) | Non-capturing group (tikai grupēt, ne saglabāt) |
, | Atsauce uz iepriekšējo grupu |
import re
# Stipra parole: 8+ simboli, vismaz 1 cipars + 1 lielais burts
parole = r"^(?=.*\d)(?=.*[A-Z]).{8,}$"
re.match(parole, "Anna1234") # match
re.match(parole, "annaanna") # None
# Atrod cenas, kuras seko EUR/$ - bez tām
re.findall(r"(?<=EUR )\d+\.\d{2}", "Cena EUR 12.50 vai USD 15.00")
# → ['12.50']
# Nosauktas grupas
m = re.match(r"(?P<diena>\d{2})\.(?P<menesis>\d{2})\.(?P<gads>\d{4})",
"31.12.2026")
print(m.group("gads")) # '2026'
| Metode | Lietojums |
|---|---|
GET | Iegūt resursu |
POST | Izveidot jaunu |
PUT | Aizstāt visu |
PATCH | Daļēji atjaunināt |
DELETE | Dzēst |
| Kods | Nozīme |
|---|---|
200 | OK |
201 | Created |
301/302 | Pāradresācija |
400 | Bad Request |
401 | Unauthorized |
403 | Forbidden |
404 | Not Found |
500 | Servera kļūda |
import requests
r = requests.get("https://api.example.com/users", params={"limit": 10})
if r.status_code == 200: dati = r.json()
requests.post("/lietotaji", json={"vards": "Anna"})
async function ieladetProfilu(id) {
const atbilde = await fetch(`/api/profils/${id}`);
if (!atbilde.ok) {
throw new Error(`API kļūda: ${atbilde.status}`);
}
return await atbilde.json();
}
ieladetProfilu(42)
.then(profils => console.log(profils.vards))
.catch(kļūda => console.error(kļūda.message));
from flask import Flask, jsonify, request
app = Flask(__name__)
speletaji = [{"id": 1, "vards": "Anna", "punkti": 120}]
@app.get("/api/speletaji")
def visi_speletaji():
return jsonify(speletaji)
@app.post("/api/speletaji")
def pievienot_speletaju():
dati = request.get_json()
jaunais = {
"id": len(speletaji) + 1,
"vards": dati["vards"],
"punkti": int(dati.get("punkti", 0)),
}
speletaji.append(jaunais)
return jsonify(jaunais), 201
# Pamata autentifikācijas tipi:
# 1. Basic Auth (vienkāršs, NEIZMANTOT bez HTTPS)
curl -u lietotajs:parole https://api.example.com/dati
# 2. Bearer Token (visbiežāk)
curl -H "Authorization: Bearer eyJhbGc..." https://api.example.com/lieto
# 3. API Key (cookie vai header)
curl -H "X-API-Key: ak_live_abc123" https://api.example.com
# 4. OAuth2 plūsma:
# Lietotājs → autorizē → kods → maina pret access_token
# JWT (JSON Web Token) sastāv no 3 daļām, atdalītas ar punktu:
# header.payload.signature
# Bāze64-kodēts, var lasīt klients, bet servera puse pārbauda signature
import jwt # pip install PyJWT
SECRET = "augsts_drosibas_atslega"
# Izveidot token
token = jwt.encode(
{"user_id": 42, "exp": 1735689600},
SECRET, algorithm="HS256"
)
# Pārbaudīt
try:
data = jwt.decode(token, SECRET, algorithms=["HS256"])
user_id = data["user_id"]
except jwt.ExpiredSignatureError:
print("Token derīgums beidzies")
except jwt.InvalidTokenError:
print("Nederīgs token")
Drošības principi: īss derīguma laiks (15 min), refresh tokens ilgākai sesijai, HTTPS vienmēr.
pickle no neuzticamiem avotiem.npm audit, Dependabot.# SLIKTI - SQL injekcija
cur.execute(f"SELECT * FROM users WHERE id = {user_input}")
# LABI - parametrizēts
cur.execute("SELECT * FROM users WHERE id = %s", (user_input,))
import bcrypt
salts = bcrypt.gensalt()
hash_val = bcrypt.hashpw(parole.encode(), salts)
bcrypt.checkpw(ievadita.encode(), hash_val)
import os
SECRET_KEY = os.environ.get("SECRET_KEY")
DATABASE_URL = os.environ.get("DATABASE_URL")
if not SECRET_KEY:
raise RuntimeError("Nav iestatīts SECRET_KEY")
# .env failu drīkst turēt lokāli, bet nedrīkst publicēt GitHub.
# .gitignore:
# .env
# *.key
// SLIKTI: lietotājs var ievadīt <script>...</script>
output.innerHTML = komentars;
// LABI: teksts tiek parādīts kā teksts, nevis HTML
output.textContent = komentars;
import bcrypt
from secrets import token_urlsafe
# REGISTRĀCIJA - saglabā paroles hash, NEKAD plain text
def registret(parole_plain: str) -> bytes:
salts = bcrypt.gensalt(rounds=12) # 12 = balanss starp drošību un ātrumu
return bcrypt.hashpw(parole_plain.encode(), salts)
stored_hash = registret("Lielparole123!")
# Datubāzē glabājas: $2b$12$XwGUDDP3M2JcvqGY...
# PIETEIKŠANĀS - salīdzini ar saglabāto hash
def pieteikties(parole_plain: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(parole_plain.encode(), stored_hash)
# Drošu paroļu kritēriji
def stipra_parole(p: str) -> bool:
if len(p) < 12: return False
if not any(c.isupper() for c in p): return False
if not any(c.isdigit() for c in p): return False
if not any(c in "!@#$%^&*" for c in p): return False
return True
# Drošs sesijas token
session_id = token_urlsafe(32) # ~256 bit nejaušums
# Two-Factor Authentication (2FA) ar TOTP
import pyotp
totp = pyotp.TOTP("base32secret3232")
totp.now() # pašreizējais 6-ciparu kods
totp.verify("123456") # validē lietotāja ievadi
Kritiskie noteikumi: nekad nelieto MD5/SHA1 paroles; vienmēr salt; lieto rate limiting pret brute-force; loģē neveiksmīgus pieteikumus.
| Pakalpojums | Kam piemērots |
|---|---|
| GitHub Pages | Statiskas HTML/CSS/JS lapas |
| Netlify / Vercel | SPA, statiskas lapas, vienkāršas API |
| Railway / Render | Python/Node apps + PostgreSQL |
| Streamlit Cloud | Streamlit aplikācijas |
| Supabase / Neon | Bezmaksas PostgreSQL |
# GitHub Pages
git push
# GitHub.com → Settings → Pages → Source: main → /(root)
# https://lietotajs.github.io/repo
# .env (NEPUSH GitHub-ā!)
DATABASE_URL=postgresql://user:pass@host/db
SECRET_KEY=randomstring
# Pirms publicēšanas
git status
git diff --check
python -m pytest
# Statiskai lapai pārbaudi lokāli
python3 -m http.server 8000
# Pēc publicēšanas pārbaudi:
# 1) vai nav 404 saišu
# 2) vai darbojas mobilajā skatā
# 3) vai sitemap.xml satur jaunās lapas
# Dockerfile - image receptne
# FROM bāzes_image
# RUN komandas image būvēšanai
# COPY ielādē failus
# CMD palaišanas komanda
# Piemērs Python Flask app
cat > Dockerfile << 'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
EOF
# Build un palaišana
docker build -t mans-app:1.0 .
docker run -d -p 5000:5000 --name mans-app mans-app:1.0
# Pārvaldība
docker ps # darbojošies containers
docker logs mans-app # logu skats
docker stop mans-app # apstādina
docker rm mans-app # noņem
docker images # uzskaita images
# docker-compose.yml - vairāku containers konfigurācija
cat > docker-compose.yml << 'EOF'
services:
web:
build: .
ports: ["5000:5000"]
depends_on: [db]
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: parole
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
EOF
docker compose up -d # palaiž visu
docker compose down # apstādina
print(f"x={x}").import pdb; pdb.set_trace()
# Komandas: n=next, s=step, c=continue, p mainīgais
import logging
logging.basicConfig(level=logging.DEBUG)
logging.info("Spēle sākas")
logging.warning("Datne nav atrasta")
logging.error("DB nepieslēdzas")
console.log("Vienkārša ziņa");
console.table(speletaji);
console.error("Kļūda!");
console.time("aprekins"); /* kods */ console.timeEnd("aprekins");
debugger;
git bisect start
git bisect bad # pašreizējā versija ir bojāta
git bisect good v1.0.0 # zināma laba versija
# Git pārslēgs commit; katrā solī palaid testu:
python -m pytest
git bisect good # ja tests iziet
git bisect bad # ja tests krīt
git bisect reset # atgriezties sākumā
import logging
import json
from datetime import datetime
# Pamata konfigurācija
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler() # arī uz konsoli
]
)
logger = logging.getLogger(__name__)
logger.debug("Sīkdetaļas") # parasti slēpts produkcijā
logger.info("Spēle sākas") # noklusējuma līmenis
logger.warning("Datne neeksistē")
logger.error("DB nepieslēdzas", exc_info=True)
logger.critical("Sistēma sabrūk")
# Strukturēts logging (JSON) - viegli analizēt
class JsonFormatter(logging.Formatter):
def format(self, record):
return json.dumps({
"laiks": datetime.now().isoformat(),
"limenis": record.levelname,
"modulis": record.name,
"zinjojums": record.getMessage(),
"fails": record.filename,
"rinda": record.lineno
})
# Konteksta info
logger.info("Spēlētājs pievienojās", extra={
"user_id": 42, "ip": "10.0.0.1", "wave": 3
})
# Metrikas - vienkāršs skaitītājs
from collections import Counter
metrikas = Counter()
metrikas["pieprasijumi"] += 1
metrikas["errori_500"] += 1
# Periodiski izvada uz /metrics endpoint
def metrikas_json():
return json.dumps(dict(metrikas))
5 logging līmeņi: DEBUG (sīkumi) → INFO (notikumi) → WARNING (potenciālas problēmas) → ERROR (kļūdas) → CRITICAL (sistēmas bojājumi).
Pirms labošanas nosaki, kāda tipa kļūda tā ir - tas norāda, kur meklēt. Stundu "Biežākās kļūdas" sadaļas parasti pieder kādai no šīm kategorijām:
| Kļūdas tips | Kad rodas | Tipisks piemērs | Kur meklēt |
|---|---|---|---|
| Sintakses kļūda (syntax error) |
Programma vispār nepalaižas / nekompilējas. | Trūkst :, ;, iekavas; nepareiza atkāpe. |
Rindā, ko norāda kļūda (vai vienu rindu augstāk). |
| Izpildlaika kļūda (runtime error) |
Programma sākas, bet avarē izpildes laikā. | ZeroDivisionError, NullReferenceException, IndexError. |
Traceback apakšējā rinda; vērtība, kas bija negaidīta. |
| Loģikas kļūda (logic error) |
Programma strādā, bet rezultāts ir nepareizs. | < vietā <=; + vietā -; cikls iet par vienu reizi par maz. |
Salīdzini gaidīto vs faktisko izvadi; print starprezultātus. |
| Stāvokļa kļūda (state error) |
Mainīgais/objekts ir negaidītā stāvoklī. | Score netiek atiestatīts; sarakstu pirms cikla neiztukšo; karogs paliek true. |
Kur mainīgais tiek mainīts; iniciaizācija pirms cikla. |
| Datu-formas kļūda (data-shape error) |
Dati nav tādā formā, kā kods sagaida. | "5" (str) vietā 5 (int); JSON atslēga citā nosaukumā; tukšs saraksts. |
Tipa pārveide (int()); print(type(x)); pārbaudi datu avotu. |
| Vides kļūda (environment error) |
Kods ir pareizs, bet vide nav sagatavota. | Bibliotēka nav instalēta; nepareizs faila ceļš; trūkst .env; SCons/compiler trūkst. |
Instalācija, ceļi, versijas, atļaujas - NEVIS pats kods. |
| Dizaina kļūda (design error) |
Kods strādā, bet risinājums ir slikti uzbūvēts. | Viena funkcija dara 5 lietas; dati dublēti; nav iespējams paplašināt. | Arhitektūra: kā sadalītas atbildības; vai datu modelis der. |
Diagnostikas secība: 1) vai palaižas? (sintakse/vide) → 2) vai avarē? (izpildlaiks) → 3) vai pareizs rezultāts? (loģika/stāvoklis/dati) → 4) vai labi uzbūvēts? (dizains).
Design Thinking (Dizaina domāšana) ir cilvēkcentrēta problēmu risināšanas pieeja, ko Stanford d.school formalizēja par piecu iteratīvu soļu procesu. Tā piemērojama jebkurā projektā - no spēlēm līdz mobilajām lietotnēm.
| Solis | Mērķis | Spēles izstrādes piemērs |
|---|---|---|
| 1. Empathize (Iejusties) | Saproti lietotāja vajadzības, motivāciju, sāpju punktus. | Intervē 5 skolēnus par to, kādas matemātikas spēles viņiem patīk un kāpēc. |
| 2. Define (Definēt) | Formulē konkrētu problēmas paziņojumu. | "7. klases skolēni vēlas trenēt reizināšanu, bet esošās lietotnes ir garlaicīgas un bez progresa saglabāšanas." |
| 3. Ideate (Ģenerēt idejas) | Brainstorming bez kritikas - daudz ideju, dažādas pieejas. | Saraksts ar 20 spēles mehānikām: laika trial, kosmosa kuģi, kāršu duelis, … |
| 4. Prototype (Prototips) | Vienkāršākais variants, ar ko var testēt galveno ideju. | Paper prototype: zīmē uz papīra spēles ekrānus, izspēlē "ar pirkstu". |
| 5. Test (Testēt) | Lūdz reāliem lietotājiem mēģināt, vēro, klausies. Iterē. | Dod prototipu 3 skolēniem, vēro, kur viņi apjūk → atgriežas pie Define vai Ideate. |
Galvenais princips: process nav lineārs - bieži jāatgriežas pie iepriekšējiem soļiem. Pirmajā prototipā 80% ideju izgāzīsies. Tas ir labi - labāk noskaidrot agri nekā pēc 6 mēnešu izstrādes.
# Projekta plāns ar Design Thinking
## 1. Empathize (1 nedēļa)
- Intervēt 5 mērķauditorijas pārstāvjus
- Apskatīt 3 konkurentu lietotnes
- Identificēt 3 lielākos sāpju punktus
## 2. Define
- Problem statement: "..."
- Mērķauditorija: ...
- Veiksmes kritēriji: ...
## 3. Ideate (brainstorming sesija 30 min)
- Min 15 ideju, bez kritikas
- Sagrupēt pēc tēmām
- Izvēlēties 3 labākās testēšanai
## 4. Prototype (2 nedēļas)
- Paper prototype → Figma mockup → MVP kods
## 5. Test
- 5 lietotāji × 30 min sesijas
- Iziet uz #1 ar jauniem datiem
Game Design Document (GDD) ir spēles "ceļvedis", ko izstrādātāji un komandas locekļi seko. Garums: maziem projektiem 2–5 lapas, lielām spēlēm - 100+ lapas.
# Spēles dizaina dokuments - "Krāsu lēciens"
## 1. Pārskats (1 paragrāfs)
**Žanrs:** Hyper-casual puzzle
**Mērķauditorija:** 12+ gadu, mobilais
**Platformas:** Web (HTML5), iOS, Android
**Vidējā sesija:** 3–5 minūtes
**Unique selling point:** Pirmā puzzle spēle, kurā jākoordinē trīs kustīgas figūras.
## 2. Spēles cilpa (Core Loop)
1. Spēlētājs redz krāsu mērķi.
2. Pārvieto 3 figūras, savieno krāsas.
3. Saņem punktus → līmenis aug → grūtāks mērķis.
(atkārtojas 30 sec - 5 min)
## 3. Mehānikas
- **Galvenā:** Drag-drop ar trīs vienlaicīgiem pirkstiem.
- **Sekundārā:** Combo, ja izpilda 3 mērķus pēc kārtas.
- **Power-ups:** Laika apturēšana (3 sec), Krāsu maiņa, Hint.
## 4. Vizuālais stils
- Krāsu palete: 6 piesātinātas krāsas + tumšs fons.
- Animācijas: smooth (12 FPS papīra-stila tween).
- Fonts: Sans-serif, bold (Inter).
## 5. Audio
- Mūzika: 1 fona melodija (loop, 2 min), tempa maiņa pie combo.
- SFX: drag, drop, success, fail, level-up.
## 6. Monetizācija (ja attiecas)
- Bezmaksas ar reklāmām pēc katra 5. līmeņa.
- Premium: €2.99 noņem reklāmas.
## 7. Roadmap
- v0.1 (Sept): Core mehānika prototype
- v0.5 (Okt): 10 līmeņi, audio
- v1.0 (Nov): Publicēta web versija
- v1.1 (Dec): Mobilais build
User Story ir īss prasību apraksts no lietotāja viedokļa. Formāts:
Kā [loma], es vēlos [darbība], lai [vērtība].
## Story #12: Saglabāt progresu
**Kā** spēlētājs,
**es vēlos** automātiski saglabāt savu progresu,
**lai** varētu turpināt spēli citu reizi.
### Acceptance criteria
- [ ] Pēc katra pabeigta līmeņa progress tiek saglabāts.
- [ ] Atverot spēli, parādās "Turpināt" poga.
- [ ] Ja nav saglabāta spēle, "Turpināt" ir paslēpta.
- [ ] Saglabāšana izdodas pat, ja interneta nav (lokāla).
- [ ] Pēc Cleanup data (browser settings) - spēle sākas no nulles.
### Prioritāte: Must Have
### Lielums: 5 punkti
### Atkarības: #8 (Level system)
MoSCoW prioritātes:
| Tips | Nozīme |
|---|---|
| Must Have | Bez tā produkts neder. ~60% laika. |
| Should Have | Svarīgs, bet ne kritisks. ~20%. |
| Could Have | Jauki, ja paspēj. ~20%. |
| Won't Have | Šajā versijā nē. (Skaidri pierakstīts!) |
| Līmenis | Detalitāte | Rīks | Kad lietot |
|---|---|---|---|
| Sketch | Pildspalva uz papīra, kvadrāti+līnijas | Papīrs, balta tāfele | 5 min - agrīna ideja |
| Low-fi wireframe | Pelēktoņi, pamata layout | Excalidraw, draw.io | Diskusijas komandā |
| Hi-fi mockup | Pilna krāsa, fonts, ikonas | Figma, Penpot | Klientu prezentācija |
| Interactive prototype | Klikšķi vada uz citiem ekrāniem | Figma prototype mode | Lietotāju testēšana |
# Wireframe priekš spēles galvenā menu (ASCII)
+----------------------------------------+
| |
| [LOGO ar spēles nosaukumu] |
| |
| +------------------+ |
| | ▶ SĀKT SPĒLI | |
| +------------------+ |
| |
| +------------------+ |
| | ⚙ Iestatījumi | |
| +------------------+ |
| |
| +------------------+ |
| | ★ Rezultāti | |
| +------------------+ |
| |
| v1.0 | MIT |
+----------------------------------------+
Padoms: sāc ar zemākajiem līmeņiem. Pavadot 2 stundas uz Figma mock-up, kuru pēc tam dizaina diskusijā izmet, ir lielāks zaudējums nekā 10 min skiči.
Konkrēti, viena lapas projekta plāni, kas der kā paraugi savu projektu izveidei:
**Apraksts:** Dators izvēlas skaitli 1–100; spēlētājs min, dators dod mājienus.
**Tehnoloģijas:** Python (CLI), random modulis
**Laiks:** 1–2 stundas
**Mehānikas:** Input, salīdzināšana, while cikls, mēģinājumu skaitīšana
**MVP:** Vienkāršs cikls ar mājieniem "lielāks"/"mazāks"
**Iespējami uzlabojumi:** Difficulty (1-100, 1-1000), HighScore, GUI ar Streamlit
**Apraksts:** 4×4 režģis ar 8 simbolu pāriem. Atrodi visus pārus.
**Tehnoloģijas:** Python + Tkinter VAI JavaScript + HTML
**Laiks:** 4–6 stundas
**Datu struktūras:** list (klājs), dict (spēles statuss)
**Mehānikas:** Random shuffle, klikšķu handler, atrašanas pārbaude
**MVP:** Statisks 4×4 ar burtiem, klikšķi atklāj, sakrišana paliek atvērta
**Uzlabojumi:** Skaņa, attēli kāršu vietā, taimeris, multi-līmeņi 6×6
**Apraksts:** Klasiska 3×3 spēle ar AI pretinieku (minimax).
**Tehnoloģijas:** HTML+CSS+JavaScript (vanilla, bez framework)
**Laiks:** 6–10 stundas
**Komponenti:** Klāja stāvoklis, gājienu validators, uzvaras pārbaude, AI
**MVP:** 2-spēlētāju režīms, uzvarētāja noteikšana
**AI:** Minimax algoritms ar alpha-beta pruning
**Uzlabojumi:** Highscore localStorage, animācijas, mobilais touch
**Apraksts:** Procedurāli ģenerēta pazeme ar permadeath.
**Tehnoloģijas:** Godot 4 + C++ (GDExtension)
**Laiks:** 40–80 stundas
**Mehānikas:** Procedural generation, turn-based combat, inventory, perma-death
**Klases (OOP):** Player, Enemy → Goblins/Brute/Skeleton, Item, Dungeon
**MVP:** Viens līmenis, kustība, viens ienaidnieka tips, viena spēja
**Iterācijas:** +items → +spējas → +bosses → +meta-progress
**Apraksts:** 2-4 spēlētāju top-down šautene tiešsaistē.
**Tehnoloģijas:** Godot 4 + C++ + WebSocket servers (Node.js)
**Laiks:** 100+ stundas (komandas projekts)
**Komandas lomas:** Programmētājs ×2, dizainers, audio
**Risks:** Tīkla latency, sinhronizācijas problēmas, hackeri
**MVP:** 2 spēlētāji lokāli (split-screen)
**Iterācijas:** → LAN spēle → Tiešsaistes lobby → Matchmaking
Padoms: Vienmēr sāc ar mazāko MVP (Minimum Viable Product) - vienkāršāko versiju, ko vari spēlēt. Tikai pēc tam paplašini.
Roadmap ir augstākā līmeņa projektēšanas plāns, kas parāda, kas un kad notiks. Sastāv no milestones (starpmērķiem).
# Spēles projekta Roadmap - 12 nedēļas
## Fāze 1: Plānošana (Nedēļas 1–2)
- [x] Design thinking sesijas (5 soļi)
- [x] GDD v0.1 uzrakstīts
- [x] User stories saraksts (15 stories)
- [ ] Tehnoloģiju izvēle (Godot/Unity/Web?)
## Fāze 2: Prototype (Nedēļas 3–5) 🎯 MILESTONE: Spēlējams prototips
- [ ] Core mehānika strādā
- [ ] 1 līmenis ar visiem elementiem
- [ ] Iekšējais playtest
## Fāze 3: Content (Nedēļas 6–9) 🎯 MILESTONE: Alpha versija
- [ ] 10 līmeņi
- [ ] Audio (mūzika + 5 SFX)
- [ ] Polišs (animācijas, particles)
- [ ] 5 ārējie testētāji
## Fāze 4: Polish + Launch (Nedēļas 10–12) 🎯 MILESTONE: v1.0 publicēts
- [ ] Bug fixes no testētājiem
- [ ] Web export
- [ ] Marketing (screenshot, video, README)
- [ ] Publicēšana GitHub Pages
Gantt chart vizualizē uzdevumu ilgumu un atkarības. Bezmaksas rīki: GitHub Projects, Trello, GanttProject.
# ASCII Gantt - vienkāršs piemērs
Nedēļa: 1 2 3 4 5 6 7 8 9 10 11 12
Plānošana: ██ ██
Prototype: ██ ██ ██
Saturs: ██ ██ ██ ██
Polish: ██ ██
Launch: ██ ██
Milestones: M1 M2 M3 LAUNCH
Daudzi izstrādātāji pārvērtē: reālistisks plāns paredz 50% buferi neparedzētiem šķēršļiem (bugs, slimības, ārējie traucēkļi).
Pirms sākt projektu, identificē iespējamos riskus un sagatavo plānu, kā tos mazināt.
# Risku analīzes matrica
| Risks | Varbūtība | Ietekme | Prioritāte | Mitigācija |
|--------------------------------|-----------|---------|------------|------------|
| Komandas dalībnieks aiziet | Vidēja | Augsta | KRITISKS | Cross-training, dokumentācija |
| Tehnoloģija nestrādā kā cerēts | Augsta | Vidēja | AUGSTS | Spike (prototype) 1. nedēļā |
| Saturs nav gatavs laikā | Augsta | Augsta | KRITISKS | MVP fokuss, izgriezt ne-must-have |
| Bugs pēdējā minūtē | Augsta | Vidēja | AUGSTS | Buferis 2 nedēļas pirms launch |
| Hardware/server downtime | Zema | Augsta | VIDĒJS | Backup hostings, monitoring |
| Lietotāji negaida spēli | Vidēja | Augsta | AUGSTS | Marketing sākt 2 mēnešus iepriekš |
Formula: Prioritāte = Varbūtība × Ietekme
Programmēšanas pasaule lielākoties runā angliski. Šeit ir biežāk lietotie angļu termini, to latviskais skaidrojums un konteksts, kurā tos sastapsi ebSkola kursos. Termini ir grupēti pa jomām.
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| repository (repo) | Projekta krātuve - visi faili + izmaiņu vēsture | "Izveido jaunu GitHub repo" |
| commit | Izmaiņu "fotogrāfija" ar aprakstu | Nofiksē pabeigtu darba posmu |
| push | Augšupielādēt lokālos commit uz mākoni | Nodod darbu vērtēšanai |
| pull | Lejupielādēt jaunākās izmaiņas no mākoņa | Pirms darba sākuma komandā |
| clone | Lejupielādēt repo kopiju savā datorā | Tikai 1× projektam |
| branch | Zars - paralēla izstrādes līnija | Jauna funkcija bez galvenā koda bojāšanas |
| merge | Sapludināt - apvienot divus zarus | Pievienot funkciju main zaram |
| stage | Sagatavot failus commit'am (git add) | Izvēlies, ko iekļaut nākamajā commit |
| remote | Attālā repo adrese (parasti GitHub) | origin = noklusējuma remote |
| conflict | Konflikts - divas izmaiņas vienā rindā | Jārisina manuāli pēc merge |
| README | Projekta apraksta fails (Markdown) | Pirmais, ko lasa cits izstrādātājs |
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| tag | Birka - HTML elements, piem. <p> | Lapas struktūras pamatvienība |
| element | Elements - birka ar saturu | HTML dokumenta sastāvdaļa |
| attribute | Atribūts - birkas papildinājums, piem. href | <a href="..."> |
| selector | Selektors - CSS norāde, ko stilizēt | .karte, #logo |
| DOM | Dokumenta objektu modelis - lapa kā koks | JS maina lapu caur DOM |
| event | Notikums - klikšķis, taustiņš, ielāde | addEventListener("click", ...) |
| event listener | Notikuma klausītājs - reaģē uz notikumu | Poga reaģē uz klikšķi |
| render | Renderēt - attēlot saturu ekrānā | Pārlūks renderē HTML |
| viewport | Skatlogs - redzamais loga apgabals | Responsīvais dizains |
| responsive | Responsīvs - pielāgojas ekrāna izmēram | Mobilais vs dators |
| callback | Atzvana funkcija - izsaukta vēlāk | Tiek izsaukta pēc notikuma |
| localStorage | Lokālā krātuve pārlūkā | Saglabā highscore bez servera |
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| variable | Mainīgais - nosaukta vērtības glabātuve | score = 0 |
| function | Funkcija - atkārtoti lietojams koda bloks | def aprekini(): |
| parameter / argument | Parametrs (definīcijā) / arguments (izsaukumā) | def f(x) → f(5) |
| return | Atgriež - funkcijas rezultāts | return summa |
| loop | Cikls - atkārto darbības | for, while |
| scope | Tvērums - kur mainīgais ir pieejams | Lokālais vs globālais |
| module / import | Modulis - atsevišķs koda fails / tā ielāde | import random |
| library | Bibliotēka - gatavs cita autora kods | matplotlib, requests |
| list / array | Saraksts / masīvs - sakārtotu vērtību kopa | [1, 2, 3] |
| dictionary / map | Vārdnīca - atslēga→vērtība pāri | {"hp": 100} |
| class | Klase - objektu veidne | class Varonis: |
| object / instance | Objekts / eksemplārs - no klases izveidots | v = Varonis() |
| method | Metode - funkcija klases iekšienē | v.cizt(10) |
| inheritance | Mantošana - bērna klase pārmanto vecāku | class Burvis(Varonis) |
| exception | Izņēmums - kļūda izpildes laikā | try / except |
| compile | Kompilēt - pārvērst kodu mašīnvalodā | C++ ar SCons |
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| database | Datubāze - strukturēta datu krātuve | PostgreSQL serveris |
| table | Tabula - rindas un kolonnas | speletaji tabula |
| query | Vaicājums - SQL pieprasījums | SELECT * FROM ... |
| schema | Shēma - datubāzes struktūras apraksts | Tabulu un kolonnu definīcijas |
| primary key | Primārā atslēga - unikāls rindas ID | id SERIAL PRIMARY KEY |
| foreign key | Ārējā atslēga - norāde uz citu tabulu | Saista spēli ar spēlētāju |
| JOIN | Apvienot datus no vairākām tabulām | INNER JOIN, LEFT JOIN |
| CRUD | Create, Read, Update, Delete operācijas | Datu pamatdarbības |
| transaction | Transakcija - visi vai neviens | BEGIN ... COMMIT |
| API | Lietotnes saskarne - programmu sazināšanās | Frontend↔backend |
| REST | API stils ar HTTP metodēm + JSON | GET/POST/PUT/DELETE |
| endpoint | Galapunkts - konkrēts API URL | /api/speletaji |
| JSON | Cilvēkam lasāms datu formāts | {"vards": "Anna"} |
| request / response | Pieprasījums / atbilde | Klients prasa, serveris atbild |
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| Engine | Dzinējs - spēļu izstrādes vide | Godot 4 |
| Scene Tree | Skatu koks - objektu hierarhija | Godot kreisais panelis |
| Node | Mezgls - Godot pamatobjekts | Sprite2D, CharacterBody2D |
| Inspector | Inspektors - objekta īpašību panelis | Godot labais panelis |
| signal | Signāls - objektu paziņojums | emit_signal("died") |
| GDExtension | Mehānisms C++ koda lietošanai Godot | Kompilēta bibliotēka |
| collision | Sadursme - objektu saskare | CollisionShape2D |
| state machine | Stāvokļu mašīna - AI uzvedības modelis | IDLE → CHASE → ATTACK |
| pathfinding | Ceļa meklēšana - ap šķēršļiem | NavigationAgent2D, A* |
| object pooling | Objektu atkārtota izmantošana | Lādiņu veiktspējai |
| profiling | Profilēšana - veiktspējas mērīšana | Atrast lēno kodu |
| delta | Laiks kopš pēdējā kadra | Vienmērīga kustība |
| export / build | Eksportēt - sagatavot palaižamu versiju | Web, Windows builds |
| Termins (EN) | Skaidrojums (LV) | Konteksts |
|---|---|---|
| IDE | Integrēta izstrādes vide - koda redaktors | VS Code |
| debug / debugging | Atkļūdošana - kļūdu meklēšana un labošana | Breakpoint, print |
| deploy / deployment | Izvietošana - publicēt internetā | GitHub Pages, Render |
| input / output | Ievade / izvade | input() / print() |
| prototype | Prototips - vienkāršākā strādājošā versija | MVP - minimālais produkts |
| refactor | Pārstrukturēt kodu, nemainot uzvedību | Padarīt skaidrāku |
| dependency | Atkarība - cita nepieciešama bibliotēka | requirements.txt |
| environment variable | Vides mainīgais - slepens iestatījums | .env fails (paroles) |
| UI / UX | Lietotāja saskarne / pieredze | Pogas, izkārtojums, lietojamība |
| frontend / backend | Priekšgals (redzamais) / aizmugure (serveris) | HTML+JS vs Python+DB |
| open source | Atvērtais pirmkods - brīvi pieejams | MIT licence |
| syntax | Sintakse - valodas gramatikas noteikumi | Iekavas, atkāpes |