From f2fe2843b2e7bc9310003b9c80b44462d4b84fdc Mon Sep 17 00:00:00 2001 From: radinpirouz Date: Fri, 13 Feb 2026 00:30:44 +0330 Subject: [PATCH] Init Commit --- .dockerignore | 4 + Dockerfile | 11 + docker-compose.yml | 13 + main.py | 155 +++++++++ requirement.txt | 11 + ui/main.html | 564 +++++++++++++++++++++++++++++++++ users/users.json | 1 + users_courses/radinpirouz.json | 1 + 8 files changed, 760 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 main.py create mode 100644 requirement.txt create mode 100644 ui/main.html create mode 100644 users/users.json create mode 100644 users_courses/radinpirouz.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..864a47a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +docker-compose.yml +venv/ +__pycache__ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..20323e8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . . + +RUN pip install -r requirement.txt + +EXPOSE 1600 + +CMD [ "gunicorn", "main:app", "--bind", "0.0.0.0:1600" ] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2ee8179 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + unit-select: + image: radinpirouz-unit-selector:latest + container_name: unit_selector + volumes: + - ./data/users:/app/users + - ./data/users_courses:/app/users_courses + ports: + - 1600:1600 + +networks: + default: + name: unit-selector \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..056e2a5 --- /dev/null +++ b/main.py @@ -0,0 +1,155 @@ +import os +import uuid +import json +import uvicorn +from flask import Flask, render_template_string, request, redirect, url_for, session, flash +# --- CONFIGURATION --- +app = Flask(__name__) +app.secret_key = 'azad_university_secret_key' +USERS_FILE = 'users/users.json' + +TEMPLATE = open('ui/main.html', 'r').read() + +# --- DATA PERSISTENCE HELPER FUNCTIONS --- + +def load_users(): + """Loads users from the JSON file. Returns an empty list if file doesn't exist.""" + if not os.path.exists(USERS_FILE): + return [] + try: + with open(USERS_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return [] + +def save_users(users_list): + """Saves the current list of users to the JSON file.""" + try: + with open(USERS_FILE, 'w', encoding='utf-8') as f: + json.dump(users_list, f, ensure_ascii=False, indent=4) + except IOError as e: + print(f"Error saving data: {e}") + +def get_user_courses(username): + """Loads courses for a specific user from their JSON file.""" + user_file = f'users_courses/{username}.json' + if not os.path.exists(user_file): + return [] + try: + with open(user_file, 'r', encoding='utf-8') as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return [] + +def save_user_courses(username, courses_list): + """Saves courses for a specific user to their JSON file.""" + # Ensure directory exists + os.makedirs('users_courses', exist_ok=True) + user_file = f'users_courses/{username}.json' + try: + with open(user_file, 'w', encoding='utf-8') as f: + json.dump(courses_list, f, ensure_ascii=False, indent=4) + except IOError as e: + print(f"Error saving data: {e}") + +# --- AUTHENTICATION ROUTES --- + +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + users = load_users() + + # Check if username already exists + for user in users: + if user['username'] == username: + flash('نام کاربری قبلاً ثبت شده است', 'error') + return redirect(url_for('register')) + + # Create new user + new_user = { + 'id': str(uuid.uuid4()), + 'username': username, + 'password': password # In production, use password hashing! + } + users.append(new_user) + save_users(users) + + flash('ثبت نام موفقیت‌آمیز بود! حالا وارد شوید', 'success') + return redirect(url_for('login')) + + return render_template_string(TEMPLATE, page='register') + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + users = load_users() + + # Check credentials + for user in users: + if user['username'] == username and user['password'] == password: + session['user_id'] = user['id'] + session['username'] = user['username'] + flash(f'خوش آمدید، {username}!', 'success') + return redirect(url_for('index')) + + flash('نام کاربری یا رمز عبور اشتباه است', 'error') + return redirect(url_for('login')) + + return render_template_string(TEMPLATE, page='login') + +@app.route('/logout') +def logout(): + session.pop('user_id', None) + session.pop('username', None) + flash('با موفقیت خارج شدید', 'success') + return redirect(url_for('login')) + +# --- MAIN ROUTES --- + +@app.route('/') +def index(): + if 'user_id' not in session: + return redirect(url_for('login')) + + courses = get_user_courses(session['username']) + # Sort courses: High priority first + priority_order = {"high": 0, "normal": 1, "low": 2} + sorted_courses = sorted(courses, key=lambda x: priority_order.get(x["priority"], 1)) + return render_template_string(TEMPLATE, courses=sorted_courses, username=session['username']) + +@app.route('/add', methods=['POST']) +def add_course(): + if 'user_id' not in session: + return redirect(url_for('login')) + + courses = get_user_courses(session['username']) + new_course = { + "id": str(uuid.uuid4()), + "name": request.form.get('name'), + "code": request.form.get('code'), + "group": request.form.get('group'), + "day": request.form.get('day'), + "prof": request.form.get('prof'), + "priority": request.form.get('priority') + } + courses.append(new_course) + save_user_courses(session['username'], courses) + return redirect(url_for('index')) + +@app.route('/delete/') +def delete_course(course_id): + if 'user_id' not in session: + return redirect(url_for('login')) + + courses = get_user_courses(session['username']) + # Filter out the course with the matching ID + updated_courses = [c for c in courses if c['id'] != course_id] + save_user_courses(session['username'], updated_courses) + return redirect(url_for('index')) + diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 0000000..0dc8983 --- /dev/null +++ b/requirement.txt @@ -0,0 +1,11 @@ +blinker==1.9.0 +click==8.3.1 +Flask==3.1.2 +gunicorn==25.0.3 +h11==0.16.0 +itsdangerous==2.2.0 +Jinja2==3.1.6 +MarkupSafe==3.0.3 +packaging==26.0 +uvicorn==0.40.0 +Werkzeug==3.1.5 diff --git a/ui/main.html b/ui/main.html new file mode 100644 index 0000000..01564b9 --- /dev/null +++ b/ui/main.html @@ -0,0 +1,564 @@ + + + + + +دستیار انتخاب واحد — Azad Uni Helper + + + + + + + + + + +
+
+

دستیار انتخاب واحد

+

سرعت، دقت و آسودگی دانشجویان — انتخاب واحد بدون استرس

+
+
+ +{% if page == 'login' or page == 'register' %} + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + +
+ {% if page == 'login' %} +

ورود

+
+
+ + +
+
+ + +
+ +
+ + {% else %} +

ثبت نام

+
+
+ + +
+
+ + +
+ +
+ + {% endif %} +
+
+ +{% else %} + +
+ کاربر: {{ username }} + خروج +
+ +
+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + +
+

ثبت درس جدید

+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + {% if courses %} + {% for course in courses %} +
+
+ {{ course.name }} + + حذف + +
+ +
+
+
کد درس
+
+ + {{ course.code }} +
+
+
+
کد ارائه
+
+ + {{ course.group }} +
+
+
+ +
+
{{ course.prof }}
+
{{ course.day }}
+
+
+ {% endfor %} + {% else %} +
+ +

هنوز درسی ثبت نشده — شروع کن!

+
+ {% endif %} + +
+{% endif %} + + +
کپی شد!
+ + + + + + + diff --git a/users/users.json b/users/users.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/users/users.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/users_courses/radinpirouz.json b/users_courses/radinpirouz.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/users_courses/radinpirouz.json @@ -0,0 +1 @@ +[] \ No newline at end of file