מדריך מפורט למפתחים: API, מסד נתונים, ארכיטקטורה וטכנולוגיות
מערכת הנוכחות בנויה על ארכיטקטורה מודולרית תלת-שכבתית (Three-Tier Architecture).
טכנולוגיות: HTML5, CSS3, JavaScript ES6+, Bootstrap 5 RTL
אחראית על הממשק עם המשתמש, תצוגה, וחוויית משתמש.
טכנולוגיות: Python 3.11, Flask 3.0, Flask-Login, Flask-SQLAlchemy
מכילה את כל הלוגיקה העסקית, API endpoints, אימות והרשאות.
טכנולוגיות: PostgreSQL 15, SQLAlchemy ORM
ניהול ואחסון הנתונים עם תמיכה בטרנזקציות ושלמות מידע.
app/
├── models_attendance.py # מודלים של מסד הנתונים
├── api_attendance.py # API endpoints
├── routes_attendance.py # Flask routes (HTML pages)
├── templates/
│ └── attendance/ # 27 HTML templates
│ ├── employee-dashboard.html
│ ├── admin-dashboard.html
│ ├── check-in-out.html
│ └── ...
├── static/
│ ├── css/
│ │ └── modules/
│ │ └── attendance.css
│ ├── js/
│ │ └── modules/
│ │ └── attendance-manager.js
│ └── docs/
│ └── attendance/ # Documentation
└── migrations/ # Database migrations
המערכת משתמשת ב-9 טבלאות עיקריות, כולן מחוברות דרך Foreign Keys.
תיאור: רשומות כניסה ויציאה של עובדים
| שדה | טיפוס | אילוצים | תיאור |
|---|---|---|---|
id |
Integer | Primary Key | מזהה ייחודי |
user_id |
Integer | Foreign Key → users.id | מזהה עובד |
check_in_time |
DateTime | Not Null | זמן כניסה |
check_out_time |
DateTime | Nullable | זמן יציאה |
location_checkin |
String | Nullable | מיקום GPS בכניסה |
location_checkout |
String | Nullable | מיקום GPS ביציאה |
status |
String(20) | Default: 'present' | סטטוס: present/absent/leave |
notes |
Text | Nullable | הערות |
created_at |
DateTime | Default: now() | תאריך יצירה |
# Example query
from app.models_attendance import AttendanceRecord
# Get today's attendance for a user
today_attendance = AttendanceRecord.query.filter(
AttendanceRecord.user_id == user_id,
func.date(AttendanceRecord.check_in_time) == date.today()
).first()
תיאור: בקשות חופשה של עובדים
| שדה | טיפוס | אילוצים | תיאור |
|---|---|---|---|
id |
Integer | Primary Key | מזהה ייחודי |
user_id |
Integer | Foreign Key → users.id | מזהה עובד |
leave_type |
String(50) | Not Null | סוג: vacation/sick/unpaid |
start_date |
Date | Not Null | תאריך התחלה |
end_date |
Date | Not Null | תאריך סיום |
status |
String(20) | Default: 'pending' | סטטוס: pending/approved/rejected |
reason |
Text | Nullable | סיבה לבקשה |
approved_by |
Integer | Foreign Key → users.id | מזהה מאשר |
approved_at |
DateTime | Nullable | מתי אושר |
תיאור: יתרות חופשה לכל עובד
| שדה | טיפוס | תיאור |
|---|---|---|
id |
Integer | מזהה ייחודי |
user_id |
Integer | Foreign Key → users.id |
year |
Integer | שנה |
total_days |
Integer | סה"כ ימים |
used_days |
Integer | ימים שנוצלו |
remaining_days |
Integer | ימים נותרים (מחושב) |
users דרך Foreign Keys עם CASCADE על מחיקה.
המערכת מספקת 20+ API endpoints מאובטחים עם אימות וניהול הרשאות.
# All API endpoints require authentication
from flask_login import login_required, current_user
# Permission decorators
@require_role('admin', 'manager') # Only admin or manager
@require_admin # Only admin
@can_view_user_data(user_id) # Can view specific user data
/api/attendance/status
תיאור: קבלת סטטוס נוכחות נוכחי של העובד
הרשאות: משתמש מחובר
תגובה:
{
"success": true,
"data": {
"checked_in": false,
"check_in_time": null,
"check_out_time": null
}
}
/api/attendance/check-in
תיאור: ביצוע כניסה (check-in)
הרשאות: משתמש מחובר
Body:
{
"location": "32.0853,34.7818" // Optional GPS coordinates
}
תגובה מוצלחת:
{
"success": true,
"message": "נרשמת בהצלחה",
"data": {
"id": 123,
"check_in_time": "2025-11-20T08:30:00",
"location": "32.0853,34.7818"
}
}
/api/attendance/check-out
תיאור: ביצוע יציאה (check-out)
הרשאות: משתמש מחובר
Body:
{
"location": "32.0853,34.7818" // Optional
}
תגובה:
{
"success": true,
"message": "יציאה נרשמה בהצלחה",
"data": {
"id": 123,
"check_in_time": "2025-11-20T08:30:00",
"check_out_time": "2025-11-20T17:00:00",
"hours_worked": 8.5
}
}
/api/attendance/leave-requests
תיאור: קבלת רשימת בקשות חופשה
הרשאות: משתמש רואה רק את הבקשות שלו, מנהל רואה הכל
Query Parameters:
status - סנן לפי סטטוס (pending/approved/rejected)user_id - סנן לפי עובד (מנהלים בלבד)/api/attendance/leave-requests
תיאור: יצירת בקשת חופשה חדשה
Body:
{
"leave_type": "vacation",
"start_date": "2025-12-01",
"end_date": "2025-12-05",
"reason": "חופשה משפחתית"
}
Validations:
/api/attendance/leave-requests/{id}/approve
תיאור: אישור בקשת חופשה
הרשאות: מנהל או אדמין בלבד
Body:
{
"notes": "מאושר" // Optional
}
/api/attendance/leave-requests/{id}/reject
תיאור: דחיית בקשת חופשה
הרשאות: מנהל או אדמין בלבד
Body:
{
"reason": "אין אפשרות בתאריכים אלו" // Required
}
/api/attendance/admin/statistics
תיאור: סטטיסטיקות כלליות למנהלים
הרשאות: @require_role('admin', 'manager')
תגובה:
{
"success": true,
"data": {
"total_employees": 150,
"present_today": 142,
"on_leave": 8,
"late_today": 5,
"pending_requests": 12
}
}
/api/attendance/admin/today
תיאור: רשימת כל העובדים ונוכחות היום
הרשאות: @require_role('admin', 'manager')
/api/attendance/records
תיאור: קבלת רשומות נוכחות
Query Parameters:
user_id - סנן לפי עובדstart_date - מתאריךend_date - עד תאריךlimit - מספר רשומות (ברירת מחדל: 100)כל ה-API endpoints מחזירים שגיאות בפורמט אחיד:
{
"success": false,
"error": "נדרשת הזדהות",
"code": 401
}
קודי שגיאה נפוצים:
400 - Bad Request (נתונים לא תקינים)401 - Unauthorized (לא מחובר)403 - Forbidden (אין הרשאה)404 - Not Found (לא נמצא)500 - Internal Server Error (שגיאת שרת)מחלקת JavaScript מרכזית המנהלת את כל הפונקציונליות של הצד הלקוח.
// File: attendance-manager.js (583 lines)
class AttendanceManager {
constructor() {
this.attendanceStatus = null;
this.currentLocation = null;
this.init();
}
async init() {
// Initialize the manager
await this.loadAttendanceStatus();
this.setupEventListeners();
this.requestLocation();
}
// Main methods:
async loadAttendanceStatus() // GET /api/attendance/status
async performCheckIn() // POST /api/attendance/check-in
async performCheckOut() // POST /api/attendance/check-out
async loadLeaveRequests() // GET /api/attendance/leave-requests
async submitLeaveRequest() // POST /api/attendance/leave-requests
renderAttendanceStatus() // Update UI
// ... +30 more methods
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', () => {
window.attendanceManager = new AttendanceManager();
});
1. User clicks "כניסה" button
↓
2. AttendanceManager.performCheckIn()
↓
3. Show loading state (button text: "מבצע כניסה...")
↓
4. Get GPS location (if permitted)
↓
5. POST /api/attendance/check-in
↓
6. API validates: not already checked in
↓
7. Create AttendanceRecord in DB
↓
8. Return success response
↓
9. Show SweetAlert success message
↓
10. Update UI: button changes to "יציאה" (red)
↓
11. Display check-in time in card
↓
12. Reload status from server (verify)
// Extract data from API response
const response = await fetch('/api/attendance/status');
const json = await response.json();
// Handle both formats:
// New: { success: true, data: { checked_in: false } }
// Old: { checked_in: false }
const data = json.data || json;
this.attendanceStatus = data;
renderAttendanceStatus() {
const status = this.attendanceStatus;
// Determine state
const isCheckedIn = status.checked_in;
const isCheckedOut = status.check_out_time != null;
if (isCheckedOut) {
// State 3: Completed for today
statusClass = 'complete';
buttonText = 'הושלם ליום זה';
buttonDisabled = true;
buttonClass = 'btn-secondary';
} else if (isCheckedIn) {
// State 2: Checked in, can check out
statusClass = 'active';
buttonText = 'יציאה';
buttonDisabled = false;
buttonClass = 'btn-danger';
} else {
// State 1: Not checked in yet
statusClass = 'inactive';
buttonText = 'כניסה';
buttonDisabled = false;
buttonClass = 'btn-success';
}
// Update DOM
document.querySelector('button').textContent = buttonText;
}
# 1. Clone the repository
git clone https://github.com/your-org/attendance-system.git
cd attendance-system
# 2. Create virtual environment
python3 -m venv venv
source venv/bin/activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Setup database
export DATABASE_URL="postgresql://user:pass@localhost/attendance"
flask db upgrade
# 5. Run the application
flask run
# Using docker-compose
docker-compose up -d
# Check logs
docker-compose logs -f web
# Run migrations
docker-compose exec web flask db upgrade
# Create demo data
docker-compose exec web python create_attendance_demo_data.py
# .env file
DATABASE_URL=postgresql://user:pass@db:5432/techlabs
SECRET_KEY=your-secret-key-here
FLASK_ENV=production
FLASK_DEBUG=0
# Optional
ENABLE_GPS_TRACKING=true
MAX_DISTANCE_METERS=500
DEFAULT_WORK_HOURS=8
כל פעולה רגישה נרשמת ב-AttendanceAuditLog:
from app.models_attendance import AttendanceAuditLog
# Log example
audit = AttendanceAuditLog(
user_id=admin_id,
action='EDIT_ATTENDANCE',
target_user_id=employee_id,
old_value='08:30',
new_value='08:00',
reason='תיקון לבקשת עובד',
ip_address=request.remote_addr
)
db.session.add(audit)
db.session.commit()
# Example: Efficient query with JOIN
records = db.session.query(
AttendanceRecord, User
).join(
User, AttendanceRecord.user_id == User.id
).filter(
func.date(AttendanceRecord.check_in_time) == date.today()
).all()
# Instead of:
# for record in records:
# user = User.query.get(record.user_id) # N+1 problem!
# Run all tests
pytest
# Run specific test file
pytest tests/test_attendance_api.py
# Run with coverage
pytest --cov=app/api_attendance --cov-report=html
# Run UX tests (Playwright)
node test_attendance_ux.js
def test_check_in(client, auth):
# Login as employee
auth.login('david.cohen@techlabs.com', '123456')
# Perform check-in
response = client.post('/api/attendance/check-in', json={
'location': '32.0853,34.7818'
})
assert response.status_code == 200
data = response.get_json()
assert data['success'] == True
assert 'check_in_time' in data['data']
# Verify in database
record = AttendanceRecord.query.filter_by(
user_id=auth.user_id,
check_in_time__isnot=None
).first()
assert record is not None