From a0e33856703a2b8b72e05598d95d9c8eea520c69 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Tue, 28 Apr 2026 13:17:54 +0800 Subject: [PATCH] refactor: refactor for modern layout - split frontend and backend. - update backend with modern Python dev strategies. --- .gitignore | 19 +-- backend/.gitignore | 19 +++ backend/.python-version | 1 + src/sql/mysql.sql => backend/README.md | 0 backend/coconut-leaf.py | 76 ++++++++++ backend/coconut-leaf.template.toml | 22 +++ {src => backend}/config.cfg.template | 0 backend/config.py | 130 ++++++++++++++++++ {src => backend}/database.py | 62 ++++++--- {src => backend}/dt.py | 49 ++++--- backend/pyproject.toml | 14 ++ {src => backend}/server.py | 48 ++++--- backend/sql/mysql.sql | 0 {src => backend}/sql/sqlite.sql | 0 {src => backend}/utils.py | 0 backend/uv.lock | 117 ++++++++++++++++ {documents => docs}/Principle_zh-CN.md | 0 {src => frontend}/static/css/admin.css | 0 {src => frontend}/static/css/calendar.css | 0 {src => frontend}/static/css/collection.css | 0 .../static/css/datetimepicker.css | 0 {src => frontend}/static/css/event.css | 0 {src => frontend}/static/css/todo.css | 0 .../static/i18n/strings_en-US.properties | 0 .../static/i18n/strings_zh-CN.properties | 0 {src => frontend}/static/js/api.js | 0 {src => frontend}/static/js/datetime.js | 0 {src => frontend}/static/js/datetimepicker.js | 0 {src => frontend}/static/js/headerNav.js | 0 {src => frontend}/static/js/i18n.js | 0 .../static/js/localStorageAssist.js | 0 {src => frontend}/static/js/messagebox.js | 0 {src => frontend}/static/js/page/admin.js | 0 {src => frontend}/static/js/page/calendar.js | 0 .../static/js/page/collection.js | 0 {src => frontend}/static/js/page/event.js | 0 {src => frontend}/static/js/page/home.js | 0 {src => frontend}/static/js/page/login.js | 0 {src => frontend}/static/js/page/todo.js | 0 {src => frontend}/static/js/tabcontrol.js | 0 {src => frontend}/static/js/template.js | 0 {src => frontend}/static/js/utils.js | 0 .../static/tmpl/calendarItem.tmpl | 0 .../static/tmpl/datetimepicker.tmpl | 0 .../static/tmpl/displayOwnedItem.tmpl | 0 .../static/tmpl/displaySharedItem.tmpl | 0 {src => frontend}/static/tmpl/headerNav.tmpl | 0 {src => frontend}/static/tmpl/messagebox.tmpl | 0 {src => frontend}/static/tmpl/optionItem.tmpl | 0 {src => frontend}/static/tmpl/ownedItem.tmpl | 0 .../static/tmpl/scheduleItem.tmpl | 0 .../static/tmpl/sharingItem.tmpl | 0 {src => frontend}/static/tmpl/todoItem.tmpl | 0 {src => frontend}/static/tmpl/tokenItem.tmpl | 0 {src => frontend}/static/tmpl/userItem.tmpl | 0 {src => frontend}/templates/admin.html | 0 {src => frontend}/templates/calendar.html | 0 {src => frontend}/templates/collection.html | 0 {src => frontend}/templates/event.html | 0 {src => frontend}/templates/home.html | 0 {src => frontend}/templates/login.html | 0 {src => frontend}/templates/todo.html | 0 {tools => scripts}/dial_plate_gen.py | 0 src/coconut-leaf.py | 54 -------- src/config.py | 7 - 65 files changed, 479 insertions(+), 139 deletions(-) create mode 100644 backend/.gitignore create mode 100644 backend/.python-version rename src/sql/mysql.sql => backend/README.md (100%) create mode 100644 backend/coconut-leaf.py create mode 100644 backend/coconut-leaf.template.toml rename {src => backend}/config.cfg.template (100%) create mode 100644 backend/config.py rename {src => backend}/database.py (92%) rename {src => backend}/dt.py (87%) create mode 100644 backend/pyproject.toml rename {src => backend}/server.py (94%) create mode 100644 backend/sql/mysql.sql rename {src => backend}/sql/sqlite.sql (100%) rename {src => backend}/utils.py (100%) create mode 100644 backend/uv.lock rename {documents => docs}/Principle_zh-CN.md (100%) rename {src => frontend}/static/css/admin.css (100%) rename {src => frontend}/static/css/calendar.css (100%) rename {src => frontend}/static/css/collection.css (100%) rename {src => frontend}/static/css/datetimepicker.css (100%) rename {src => frontend}/static/css/event.css (100%) rename {src => frontend}/static/css/todo.css (100%) rename {src => frontend}/static/i18n/strings_en-US.properties (100%) rename {src => frontend}/static/i18n/strings_zh-CN.properties (100%) rename {src => frontend}/static/js/api.js (100%) rename {src => frontend}/static/js/datetime.js (100%) rename {src => frontend}/static/js/datetimepicker.js (100%) rename {src => frontend}/static/js/headerNav.js (100%) rename {src => frontend}/static/js/i18n.js (100%) rename {src => frontend}/static/js/localStorageAssist.js (100%) rename {src => frontend}/static/js/messagebox.js (100%) rename {src => frontend}/static/js/page/admin.js (100%) rename {src => frontend}/static/js/page/calendar.js (100%) rename {src => frontend}/static/js/page/collection.js (100%) rename {src => frontend}/static/js/page/event.js (100%) rename {src => frontend}/static/js/page/home.js (100%) rename {src => frontend}/static/js/page/login.js (100%) rename {src => frontend}/static/js/page/todo.js (100%) rename {src => frontend}/static/js/tabcontrol.js (100%) rename {src => frontend}/static/js/template.js (100%) rename {src => frontend}/static/js/utils.js (100%) rename {src => frontend}/static/tmpl/calendarItem.tmpl (100%) rename {src => frontend}/static/tmpl/datetimepicker.tmpl (100%) rename {src => frontend}/static/tmpl/displayOwnedItem.tmpl (100%) rename {src => frontend}/static/tmpl/displaySharedItem.tmpl (100%) rename {src => frontend}/static/tmpl/headerNav.tmpl (100%) rename {src => frontend}/static/tmpl/messagebox.tmpl (100%) rename {src => frontend}/static/tmpl/optionItem.tmpl (100%) rename {src => frontend}/static/tmpl/ownedItem.tmpl (100%) rename {src => frontend}/static/tmpl/scheduleItem.tmpl (100%) rename {src => frontend}/static/tmpl/sharingItem.tmpl (100%) rename {src => frontend}/static/tmpl/todoItem.tmpl (100%) rename {src => frontend}/static/tmpl/tokenItem.tmpl (100%) rename {src => frontend}/static/tmpl/userItem.tmpl (100%) rename {src => frontend}/templates/admin.html (100%) rename {src => frontend}/templates/calendar.html (100%) rename {src => frontend}/templates/collection.html (100%) rename {src => frontend}/templates/event.html (100%) rename {src => frontend}/templates/home.html (100%) rename {src => frontend}/templates/login.html (100%) rename {src => frontend}/templates/todo.html (100%) rename {tools => scripts}/dial_plate_gen.py (100%) delete mode 100644 src/coconut-leaf.py delete mode 100644 src/config.py diff --git a/.gitignore b/.gitignore index d59593c..3b1bc95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,3 @@ -# ignore sqlite db -*.db - -# ignore my debug setting -*.cfg - -# ignore py cache -src/__pycache__ - -# ignore any image first -*.png -*.jpg -*.gif - -# elimate vscode -.vscode \ No newline at end of file +## ======== Personal ======== +# Ignore VSCode +.vscode/ diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..09347df --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,19 @@ +## ======== Personal ======== +# Database file +*.db + +# Ignore setting file +coconut-leaf.toml + +## ======== Python ======== +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv + diff --git a/backend/.python-version b/backend/.python-version new file mode 100644 index 0000000..2c07333 --- /dev/null +++ b/backend/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/src/sql/mysql.sql b/backend/README.md similarity index 100% rename from src/sql/mysql.sql rename to backend/README.md diff --git a/backend/coconut-leaf.py b/backend/coconut-leaf.py new file mode 100644 index 0000000..02f31fd --- /dev/null +++ b/backend/coconut-leaf.py @@ -0,0 +1,76 @@ +import sys +import logging +from argparse import ArgumentParser +from typing import cast +from pathlib import Path +import server +import config +import utils +import database + + +def GetUsernamePassword(): + print("What is the first username of this calendar system?") + cache = input() + while not utils.IsValidUsername(cache): + print("Sorry, invalid data. Please try again.") + cache = input() + username = cache + + print("Input this user password:") + cache = input() + while not utils.IsValidPassword(cache): + print("Sorry, invalid data. Please try again.") + cache = input() + password = cache + + return (username, password) + + +if __name__ == "__main__": + print("Coconut-leaf") + print("A self-host, multi-account calendar system.") + print("Project: https://github.com/yyc12345/coconut-leaf") + print("===================") + + # Receive arguments + parser = ArgumentParser(description="Coconut-leaf") + parser.add_argument( + "-c", + "--config", + required=True, + type=Path, + action="store", + metavar="CONFIG_TOML", + dest="config", + help="The configuration file for coconut-leaf", + ) + parser.add_argument( + "-i", + "--init", + action="store_true", + dest="init", + help="Set for initialize the calendar system", + ) + args = parser.parse_args() + + # Load config file + try: + config.setup_config(cast(Path, args.config)) + except Exception as e: + print(f"Error loading config file: {e}") + sys.exit(1) + + # Setup logging level + logging_level = logging.DEBUG if config.get_config().others.debug else logging.INFO + logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging_level) + + # Initialize the calendar system if needed + if cast(bool, args.init): + gotten_data = GetUsernamePassword() + calendar = database.CalendarDatabase() + calendar.init(*gotten_data) + calendar.close() + + logging.info("Staring server...") + server.run() diff --git a/backend/coconut-leaf.template.toml b/backend/coconut-leaf.template.toml new file mode 100644 index 0000000..11dc165 --- /dev/null +++ b/backend/coconut-leaf.template.toml @@ -0,0 +1,22 @@ +[database] +driver = "sqlite" + +[database.config] +path = "coconut-leaf.db" + +# [database] +# driver = "mysql" +# +# [database.config] +# host = "localhost" +# port = 3306 +# user = "root" +# password = "password" +# database = "coconut_leaf" + +[web] +port = 8888 + +[others] +auto-token-clean-duration = 86400 +debug = true diff --git a/src/config.cfg.template b/backend/config.cfg.template similarity index 100% rename from src/config.cfg.template rename to backend/config.cfg.template diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..33eebcb --- /dev/null +++ b/backend/config.py @@ -0,0 +1,130 @@ +import tomllib +from dataclasses import dataclass +from enum import StrEnum +from typing import Optional +from pathlib import Path + + +class DatabaseDriver(StrEnum): + SQLITE = "sqlite" + MYSQL = "mysql" + + @staticmethod + def from_raw(raw: dict): + return DatabaseDriver(raw["driver"]) + + +@dataclass(frozen=True) +class SqliteDatabaseConfig: + path: str + """Database path""" + + @staticmethod + def from_raw(raw: dict): + return SqliteDatabaseConfig(raw["path"]) + + +@dataclass(frozen=True) +class MysqlDatabaseConfig: + host: str + """Database host""" + port: int + """Database port""" + user: str + """Database user""" + password: str + """Database password""" + database: str + """Database name""" + + @staticmethod + def from_raw(raw: dict): + return MysqlDatabaseConfig( + raw["host"], raw["port"], raw["user"], raw["password"], raw["database"] + ) + + +@dataclass(frozen=True) +class DatabaseConfig: + driver: DatabaseDriver + """Database driver""" + config: SqliteDatabaseConfig | MysqlDatabaseConfig + """Database config""" + + @staticmethod + def from_raw(raw: dict): + if raw["driver"] == DatabaseDriver.SQLITE: + return DatabaseConfig( + DatabaseDriver.SQLITE, SqliteDatabaseConfig.from_raw(raw["config"]) + ) + elif raw["driver"] == DatabaseDriver.MYSQL: + return DatabaseConfig( + DatabaseDriver.MYSQL, MysqlDatabaseConfig.from_raw(raw["config"]) + ) + else: + raise ValueError("Invalid database driver") + + +@dataclass(frozen=True) +class WebConfig: + port: int + """Web server port""" + + @staticmethod + def from_raw(raw: dict): + return WebConfig(raw["port"]) + + +@dataclass(frozen=True) +class OthersConfig: + debug: bool + """Whether enable debug mode""" + auto_token_clean_duration: int + """Auto token clean duration""" + + @staticmethod + def from_raw(raw: dict): + return OthersConfig(raw["debug"], raw["auto-token-clean-duration"]) + + +@dataclass(frozen=True) +class Config: + database: DatabaseConfig + web: WebConfig + others: OthersConfig + + @staticmethod + def from_raw(raw: dict): + return Config( + database=DatabaseConfig.from_raw(raw["database"]), + web=WebConfig.from_raw(raw["web"]), + others=OthersConfig.from_raw(raw["others"]), + ) + + +_CONFIG: Optional[Config] = None + + +def setup_config(p: Path) -> None: + """ + Setup config by given path. + + Raise exception if config file is invalid. + """ + with open(p, "rb") as f: + raw = tomllib.load(f) + + global _CONFIG + _CONFIG = Config.from_raw(raw) + + +def get_config() -> Config: + """ + Get config instance. + + Raises RuntimeError if config is not loaded. + """ + if _CONFIG is None: + raise RuntimeError("Config is not loaded. Call setup_config() first.") + else: + return _CONFIG diff --git a/src/database.py b/backend/database.py similarity index 92% rename from src/database.py rename to backend/database.py index d8ed454..0685021 100644 --- a/src/database.py +++ b/backend/database.py @@ -1,13 +1,16 @@ import config import sqlite3 -import json import utils import threading import logging import dt +from typing import cast +from pathlib import Path def SafeDatabaseOperation(func): - def wrapper(self, *args, **kwargs): + def wrapper(self: 'CalendarDatabase', *args, **kwargs): + cfg = config.get_config() + with self.mutex: # check database and acquire cursor try: @@ -15,16 +18,16 @@ def SafeDatabaseOperation(func): self.cursor = self.db.cursor() except Exception as e: self.cursor = None - if config.CustomConfig['debug']: + if cfg.others.debug: logging.exception(e) return (False, str(e), None) # do real data work try: currentTime = utils.GetCurrentTimestamp() - if currentTime - self.latestClean > config.CustomConfig['auto-token-clean-duration']: + if currentTime - self.latestClean > cfg.others.auto_token_clean_duration: self.latestClean = currentTime - print('Cleaning outdated token...') + logging.info('Cleaning outdated token...') self.tokenOper_clean() result = (True, '', func(self, *args, **kwargs)) @@ -36,13 +39,19 @@ def SafeDatabaseOperation(func): self.cursor.close() self.cursor = None self.db.rollback() - if config.CustomConfig['debug']: + if cfg.others.debug: logging.exception(e) return (False, str(e), None) return wrapper -class CalendarDatabase(object): +class CalendarDatabase: + + db: sqlite3.Connection + cursor: sqlite3.Cursor + mutex: threading.Lock + latestClean: int + def __init__(self): self.db = None self.cursor = None @@ -53,23 +62,36 @@ class CalendarDatabase(object): if (self.is_database_valid()): raise Exception('Databade is opened') - if config.CustomConfig['database-type'] == 'sqlite': - self.db = sqlite3.connect(config.CustomConfig['database-config']['url'], check_same_thread = False) - self.db.execute('PRAGMA encoding = "UTF-8";') - self.db.execute('PRAGMA foreign_keys = ON;') - elif config.CustomConfig['database-type'] == 'mysql': - raise Exception('Not implemented database') - else: - raise Exception('Unknow database type') + cfg = config.get_config() + match cfg.database.driver: + case config.DatabaseDriver.SQLITE: + self.db = sqlite3.connect(cast(config.SqliteDatabaseConfig, cfg.database.config).path, check_same_thread = False) + self.db.execute('PRAGMA encoding = "UTF-8";') + self.db.execute('PRAGMA foreign_keys = ON;') + case config.DatabaseDriver.MYSQL: + raise Exception('Not implemented database') + case _: + raise Exception('Unknow database type') def init(self, username, password): if (self.is_database_valid()): raise Exception('Database is opened') # establish tables + cfg = config.get_config() + backend_path = Path(__file__).resolve().parent + backend_sql_path = backend_path / 'sql' + match cfg.database.driver: + case config.DatabaseDriver.SQLITE: + sql_file = backend_sql_path / 'sqlite.sql' + case config.DatabaseDriver.MYSQL: + raise Exception('Not implemented database') + case _: + raise Exception('Unknow database type') + self.open() cursor = self.db.cursor() - with open('sql/sqlite.sql', 'r', encoding='utf-8') as fsql: + with open(sql_file, 'r', encoding='utf-8') as fsql: cursor.executescript(fsql.read()) # finish init @@ -272,11 +294,11 @@ class CalendarDatabase(object): sqlList.append('[ccn_loopDateTimeStart] = ?') argumentsList.append(analyseData[5]) sqlList.append('[ccn_loopDateTimeEnd] = ?') - argumentsList.append(dt.ResolveLoopStr( + argumentsList.append(str(dt.ResolveLoopStr( analyseData[8], analyseData[5], analyseData[7] - )) + ))) # execute argumentsList.append(uuid) @@ -531,8 +553,8 @@ class CalendarDatabase(object): argumentsList.append(_username) self.cursor.execute('UPDATE user SET {} WHERE [ccn_name] = ?;'.format(', '.join(sqlList)), tuple(argumentsList)) - print(cache) - print(tuple(argumentsList)) + logging.debug(cache) + logging.debug(tuple(argumentsList)) if self.cursor.rowcount != 1: raise Exception('Fail to update due to no matched rows or too much rows.') return True diff --git a/src/dt.py b/backend/dt.py similarity index 87% rename from src/dt.py rename to backend/dt.py index 193e4a4..2f9066b 100644 --- a/src/dt.py +++ b/backend/dt.py @@ -1,6 +1,7 @@ import datetime -import time import re +import logging +import typing from functools import reduce import utils @@ -13,7 +14,9 @@ MAX_TIMESTAMP = int(MAX_DATETIME.timestamp() / 60) DAY1_SPAN = 60 * 24 DAY7_SPAN = 7 * DAY1_SPAN -def ResolveLoopStr(strl, starttime, tzoffset): +LoopHandle = typing.Callable[[re.Match, int, int, int], int] + +def ResolveLoopStr(strl: str, starttime: int, tzoffset: int) -> int: # check no loop if strl == '': return starttime @@ -40,7 +43,7 @@ def ResolveLoopStr(strl, starttime, tzoffset): raise Exception('Invalid loopRules') -def LoopHandle_Year(searchResult, starttime, times, tzoffset): +def LoopHandle_Year(searchResult: re.Match, starttime: int, times: int, tzoffset: int) -> int: clientDate = datetime.datetime.fromtimestamp(starttime * 60, UTCTimezone(tzoffset)) isStrict = searchResult.group(1) == 'S' yearSpan = int(searchResult.group(2)) @@ -52,7 +55,7 @@ def LoopHandle_Year(searchResult, starttime, times, tzoffset): if clientMonth == 2 and clientDay == 29: if isStrict: realSpan = utils.LCM(yearSpan, 4) - print(realSpan) + logging.debug(realSpan) valCache = starttime while valCache < MAX_TIMESTAMP and times > 0: newYear += realSpan @@ -71,7 +74,7 @@ def LoopHandle_Year(searchResult, starttime, times, tzoffset): val = starttime + DAY1_SPAN * (DaysCount(newYear, newMonth, newDay) - DaysCount(clientYear, clientMonth, clientDay)) return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP -def LoopHandle_Month(searchResult, starttime, times, tzoffset): +def LoopHandle_Month(searchResult: re.Match, starttime: int, times: int, tzoffset: int) -> int: isStrict = searchResult.group(1) == 'S' loopType = searchResult.group(2) monthSpan = int(searchResult.group(3)) @@ -144,7 +147,7 @@ def LoopHandle_Month(searchResult, starttime, times, tzoffset): val = starttime + DAY1_SPAN * (DaysCount(newYear, newMonth, newDay) - DaysCount(clientYear, clientMonth, clientDay)) return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP -def LoopHandle_Week(searchResult, starttime, times, tzoffset): +def LoopHandle_Week(searchResult: re.Match, starttime: int, times: int, tzoffset: int) -> int: weekOccupied = tuple(map(lambda x: x == 'T', searchResult.group(1))) weekEventCount = reduce(lambda x, y: x + (1 if y else 0), weekOccupied, 0) if weekEventCount == 0: @@ -170,25 +173,25 @@ def LoopHandle_Week(searchResult, starttime, times, tzoffset): val -= 1 return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP -def LoopHandle_Day(searchResult, starttime, times, tzoffset): +def LoopHandle_Day(searchResult: re.Match, starttime: int, times: int, tzoffset: int) -> int: val = starttime + DAY1_SPAN * times * int(searchResult.group(1)) val -= 1 return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP -precompiledLoopRules = ( +precompiledLoopRules: tuple[tuple[re.Pattern, LoopHandle], ...] = ( (re.compile(r'^Y([SR]{1})([1-9]\d*)$'), LoopHandle_Year), (re.compile(r'^M([SR]{1})([ABCD]{1})([1-9]\d*)$'), LoopHandle_Month), (re.compile(r'^W([TF]{7})([1-9]\d*)$'), LoopHandle_Week), (re.compile(r'^D([1-9]\d*)$'), LoopHandle_Day) ) -precompiledLoopStopRules = { +precompiledLoopStopRules: dict[str, re.Pattern] = { 'infinity': re.compile(r'^F$'), 'datetime': re.compile(r'^D([1-9]\d*|0)$'), 'times': re.compile(r'^T([1-9]\d*)$') } -def LeapYearCountEx(endYear, includeThis = False, baseYear = 1, includeBase = True): +def LeapYearCountEx(endYear: int, includeThis: bool = False, baseYear: int = 1, includeBase: bool = True): if not includeThis: endYear -= 1 if includeBase: @@ -204,10 +207,10 @@ def LeapYearCountEx(endYear, includeThis = False, baseYear = 1, includeBase = Tr return (endly - basely) -def LeapYearCount(year): +def LeapYearCount(year: int): return LeapYearCountEx(year, False, 1, True) -def IsLeapYear(year): +def IsLeapYear(year: int): isLeap = False if year % 4 == 0: isLeap = True @@ -217,7 +220,7 @@ def IsLeapYear(year): isLeap = True return isLeap -def DaysCount(year, month, day): +def DaysCount(year: int, month: int, day: int): ly = LeapYearCountEx(year, False, 1, True) days = 365 * (year - 1) days += ly @@ -231,7 +234,7 @@ def DaysCount(year, month, day): days += day - 1 return days -def DayOfWeek(year, month, day): +def DayOfWeek(year: int, month: int, day: int): # as we know, 1/1/1900 is Monday. # via this method, we can got 1/1/1 is Monday # compute day span @@ -240,7 +243,7 @@ def DayOfWeek(year, month, day): # return day of week (from 0 - 6, corresponding with python) return days % 7 -def GetDayInMonth(year, month, day): +def GetDayInMonth(year: int, month: int, day: int): days = MonthDayCount[month - 1] + (1 if (month == 2 and IsLeapYear(year)) else 0) firstDayOfWeek = DayOfWeek(year, month, 1) dayOfWeek = (firstDayOfWeek + day - 1) % 7 @@ -253,7 +256,7 @@ def GetDayInMonth(year, month, day): return (dayForwards, dayBackwards, weeksForward, dayOfWeek, weeksBackwards, dayOfWeek) -def GetMonthWeekStatistics(year, month): +def GetMonthWeekStatistics(year: int, month: int): days = MonthDayCount[month - 1] + (1 if (month == 2 and IsLeapYear(year)) else 0) firstDayOfWeek = DayOfWeek(year, month, 1) lastDayOfWeek = (firstDayOfWeek + days - 1) % 7 @@ -269,14 +272,18 @@ def GetMonthWeekStatistics(year, month): return tuple(result) class UTCTimezone(datetime.tzinfo): - def __init__(self, offset = 0): - self._offset = offset + + __offset: int + + def __init__(self, offset: int = 0): + self.__offset = offset def utcoffset(self, dt): - return datetime.timedelta(minutes=self._offset) + return datetime.timedelta(minutes=self.__offset) def tzname(self, dt): - return 'UTC {}'.format(self._offset) + return 'UTC {}'.format(self.__offset) def dst(self, dt): - return datetime.timedelta(0) \ No newline at end of file + return datetime.timedelta(0) + \ No newline at end of file diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..8e802f5 --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "coleaf-backend" +version = "1.1.0" +description = "The backend of coconut-leaf." +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "flask==2.2.3", +] +[tool.uv] +constraint-dependencies = [ + "Werkzeug==2.2.2", + "MarkupSafe==2.1.5" +] \ No newline at end of file diff --git a/src/server.py b/backend/server.py similarity index 94% rename from src/server.py rename to backend/server.py index 951ca39..c37af1c 100644 --- a/src/server.py +++ b/backend/server.py @@ -1,39 +1,45 @@ from flask import Flask -from flask import g +# from flask import g from flask import render_template from flask import url_for from flask import request -from flask import abort +# from flask import abort from flask import redirect -from functools import reduce -import json -import os +# from functools import reduce +# import json +# import os import config import database import utils +from pathlib import Path -app = Flask(__name__) +_FRONTEND_PATH = Path(__file__).resolve().parent.parent / "frontend" +app = Flask( + __name__, + static_folder=_FRONTEND_PATH / "static", + template_folder=_FRONTEND_PATH / "templates", +) calendar_db = database.CalendarDatabase() # render_static_resources = None # =============================================database -''' -def get_database(): - db = getattr(g, '_database', None) - if db is None: - db = database.CalendarDatabase() - db.open() - return db -@app.teardown_appcontext -def close_database(exception): - db = getattr(g, '_database', None) - if db is not None: - db.close() -''' +# def get_database(): +# db = getattr(g, '_database', None) +# if db is None: +# db = database.CalendarDatabase() +# db.open() +# return db + +# @app.teardown_appcontext +# def close_database(exception): +# db = getattr(g, '_database', None) +# if db is not None: +# db.close() + # ============================================= static page route @@ -216,7 +222,7 @@ def api_collection_getFullOwnHandle(): @app.route('/api/collection/getListOwn', methods=['POST']) def api_collection_getListOwnHandle(): - return SmartDbCaller(calendar_db.collection_getListlOwn, + return SmartDbCaller(calendar_db.collection_getListOwn, (('token', str, False), ), None) @@ -446,6 +452,6 @@ def ConstructResponseBody(returnedTuple): def run(): calendar_db.open() - app.run(port=config.CustomConfig['web']['port']) + app.run(port=config.get_config().web.port) calendar_db.close() \ No newline at end of file diff --git a/backend/sql/mysql.sql b/backend/sql/mysql.sql new file mode 100644 index 0000000..e69de29 diff --git a/src/sql/sqlite.sql b/backend/sql/sqlite.sql similarity index 100% rename from src/sql/sqlite.sql rename to backend/sql/sqlite.sql diff --git a/src/utils.py b/backend/utils.py similarity index 100% rename from src/utils.py rename to backend/utils.py diff --git a/backend/uv.lock b/backend/uv.lock new file mode 100644 index 0000000..6d824c3 --- /dev/null +++ b/backend/uv.lock @@ -0,0 +1,117 @@ +version = 1 +revision = 2 +requires-python = ">=3.11" + +[manifest] +constraints = [ + { name = "markupsafe", specifier = "==2.1.5" }, + { name = "werkzeug", specifier = "==2.2.2" }, +] + +[[package]] +name = "click" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, +] + +[[package]] +name = "coleaf-backend" +version = "1.1.0" +source = { virtual = "." } +dependencies = [ + { name = "flask" }, +] + +[package.metadata] +requires-dist = [{ name = "flask", specifier = "==2.2.3" }] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "flask" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/5c/ff9047989bd995b1098d14b03013f160225db2282925b517bb4a967752ee/Flask-2.2.3.tar.gz", hash = "sha256:7eb373984bf1c770023fce9db164ed0c3353cd0b53f130f4693da0ca756a2e6d", size = 697599, upload-time = "2023-02-15T22:43:57.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/9c/a3542594ce4973786236a1b7b702b8ca81dbf40ea270f0f96284f0c27348/Flask-2.2.3-py3-none-any.whl", hash = "sha256:c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d", size = 101839, upload-time = "2023-02-15T22:43:55.501Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384, upload-time = "2024-02-02T16:31:22.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219, upload-time = "2024-02-02T16:30:19.988Z" }, + { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098, upload-time = "2024-02-02T16:30:21.063Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014, upload-time = "2024-02-02T16:30:22.926Z" }, + { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220, upload-time = "2024-02-02T16:30:24.76Z" }, + { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756, upload-time = "2024-02-02T16:30:25.877Z" }, + { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988, upload-time = "2024-02-02T16:30:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718, upload-time = "2024-02-02T16:30:28.111Z" }, + { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317, upload-time = "2024-02-02T16:30:29.214Z" }, + { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670, upload-time = "2024-02-02T16:30:30.915Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224, upload-time = "2024-02-02T16:30:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215, upload-time = "2024-02-02T16:30:33.081Z" }, + { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069, upload-time = "2024-02-02T16:30:34.148Z" }, + { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452, upload-time = "2024-02-02T16:30:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462, upload-time = "2024-02-02T16:30:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869, upload-time = "2024-02-02T16:30:37.834Z" }, + { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906, upload-time = "2024-02-02T16:30:39.366Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296, upload-time = "2024-02-02T16:30:40.413Z" }, + { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038, upload-time = "2024-02-02T16:30:42.243Z" }, + { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572, upload-time = "2024-02-02T16:30:43.326Z" }, + { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127, upload-time = "2024-02-02T16:30:44.418Z" }, +] + +[[package]] +name = "werkzeug" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/c1/1c8e539f040acd80f844c69a5ef8e2fccdf8b442dabb969e497b55d544e1/Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", size = 844378, upload-time = "2022-08-08T21:44:15.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5", size = 232700, upload-time = "2022-08-08T21:44:13.251Z" }, +] diff --git a/documents/Principle_zh-CN.md b/docs/Principle_zh-CN.md similarity index 100% rename from documents/Principle_zh-CN.md rename to docs/Principle_zh-CN.md diff --git a/src/static/css/admin.css b/frontend/static/css/admin.css similarity index 100% rename from src/static/css/admin.css rename to frontend/static/css/admin.css diff --git a/src/static/css/calendar.css b/frontend/static/css/calendar.css similarity index 100% rename from src/static/css/calendar.css rename to frontend/static/css/calendar.css diff --git a/src/static/css/collection.css b/frontend/static/css/collection.css similarity index 100% rename from src/static/css/collection.css rename to frontend/static/css/collection.css diff --git a/src/static/css/datetimepicker.css b/frontend/static/css/datetimepicker.css similarity index 100% rename from src/static/css/datetimepicker.css rename to frontend/static/css/datetimepicker.css diff --git a/src/static/css/event.css b/frontend/static/css/event.css similarity index 100% rename from src/static/css/event.css rename to frontend/static/css/event.css diff --git a/src/static/css/todo.css b/frontend/static/css/todo.css similarity index 100% rename from src/static/css/todo.css rename to frontend/static/css/todo.css diff --git a/src/static/i18n/strings_en-US.properties b/frontend/static/i18n/strings_en-US.properties similarity index 100% rename from src/static/i18n/strings_en-US.properties rename to frontend/static/i18n/strings_en-US.properties diff --git a/src/static/i18n/strings_zh-CN.properties b/frontend/static/i18n/strings_zh-CN.properties similarity index 100% rename from src/static/i18n/strings_zh-CN.properties rename to frontend/static/i18n/strings_zh-CN.properties diff --git a/src/static/js/api.js b/frontend/static/js/api.js similarity index 100% rename from src/static/js/api.js rename to frontend/static/js/api.js diff --git a/src/static/js/datetime.js b/frontend/static/js/datetime.js similarity index 100% rename from src/static/js/datetime.js rename to frontend/static/js/datetime.js diff --git a/src/static/js/datetimepicker.js b/frontend/static/js/datetimepicker.js similarity index 100% rename from src/static/js/datetimepicker.js rename to frontend/static/js/datetimepicker.js diff --git a/src/static/js/headerNav.js b/frontend/static/js/headerNav.js similarity index 100% rename from src/static/js/headerNav.js rename to frontend/static/js/headerNav.js diff --git a/src/static/js/i18n.js b/frontend/static/js/i18n.js similarity index 100% rename from src/static/js/i18n.js rename to frontend/static/js/i18n.js diff --git a/src/static/js/localStorageAssist.js b/frontend/static/js/localStorageAssist.js similarity index 100% rename from src/static/js/localStorageAssist.js rename to frontend/static/js/localStorageAssist.js diff --git a/src/static/js/messagebox.js b/frontend/static/js/messagebox.js similarity index 100% rename from src/static/js/messagebox.js rename to frontend/static/js/messagebox.js diff --git a/src/static/js/page/admin.js b/frontend/static/js/page/admin.js similarity index 100% rename from src/static/js/page/admin.js rename to frontend/static/js/page/admin.js diff --git a/src/static/js/page/calendar.js b/frontend/static/js/page/calendar.js similarity index 100% rename from src/static/js/page/calendar.js rename to frontend/static/js/page/calendar.js diff --git a/src/static/js/page/collection.js b/frontend/static/js/page/collection.js similarity index 100% rename from src/static/js/page/collection.js rename to frontend/static/js/page/collection.js diff --git a/src/static/js/page/event.js b/frontend/static/js/page/event.js similarity index 100% rename from src/static/js/page/event.js rename to frontend/static/js/page/event.js diff --git a/src/static/js/page/home.js b/frontend/static/js/page/home.js similarity index 100% rename from src/static/js/page/home.js rename to frontend/static/js/page/home.js diff --git a/src/static/js/page/login.js b/frontend/static/js/page/login.js similarity index 100% rename from src/static/js/page/login.js rename to frontend/static/js/page/login.js diff --git a/src/static/js/page/todo.js b/frontend/static/js/page/todo.js similarity index 100% rename from src/static/js/page/todo.js rename to frontend/static/js/page/todo.js diff --git a/src/static/js/tabcontrol.js b/frontend/static/js/tabcontrol.js similarity index 100% rename from src/static/js/tabcontrol.js rename to frontend/static/js/tabcontrol.js diff --git a/src/static/js/template.js b/frontend/static/js/template.js similarity index 100% rename from src/static/js/template.js rename to frontend/static/js/template.js diff --git a/src/static/js/utils.js b/frontend/static/js/utils.js similarity index 100% rename from src/static/js/utils.js rename to frontend/static/js/utils.js diff --git a/src/static/tmpl/calendarItem.tmpl b/frontend/static/tmpl/calendarItem.tmpl similarity index 100% rename from src/static/tmpl/calendarItem.tmpl rename to frontend/static/tmpl/calendarItem.tmpl diff --git a/src/static/tmpl/datetimepicker.tmpl b/frontend/static/tmpl/datetimepicker.tmpl similarity index 100% rename from src/static/tmpl/datetimepicker.tmpl rename to frontend/static/tmpl/datetimepicker.tmpl diff --git a/src/static/tmpl/displayOwnedItem.tmpl b/frontend/static/tmpl/displayOwnedItem.tmpl similarity index 100% rename from src/static/tmpl/displayOwnedItem.tmpl rename to frontend/static/tmpl/displayOwnedItem.tmpl diff --git a/src/static/tmpl/displaySharedItem.tmpl b/frontend/static/tmpl/displaySharedItem.tmpl similarity index 100% rename from src/static/tmpl/displaySharedItem.tmpl rename to frontend/static/tmpl/displaySharedItem.tmpl diff --git a/src/static/tmpl/headerNav.tmpl b/frontend/static/tmpl/headerNav.tmpl similarity index 100% rename from src/static/tmpl/headerNav.tmpl rename to frontend/static/tmpl/headerNav.tmpl diff --git a/src/static/tmpl/messagebox.tmpl b/frontend/static/tmpl/messagebox.tmpl similarity index 100% rename from src/static/tmpl/messagebox.tmpl rename to frontend/static/tmpl/messagebox.tmpl diff --git a/src/static/tmpl/optionItem.tmpl b/frontend/static/tmpl/optionItem.tmpl similarity index 100% rename from src/static/tmpl/optionItem.tmpl rename to frontend/static/tmpl/optionItem.tmpl diff --git a/src/static/tmpl/ownedItem.tmpl b/frontend/static/tmpl/ownedItem.tmpl similarity index 100% rename from src/static/tmpl/ownedItem.tmpl rename to frontend/static/tmpl/ownedItem.tmpl diff --git a/src/static/tmpl/scheduleItem.tmpl b/frontend/static/tmpl/scheduleItem.tmpl similarity index 100% rename from src/static/tmpl/scheduleItem.tmpl rename to frontend/static/tmpl/scheduleItem.tmpl diff --git a/src/static/tmpl/sharingItem.tmpl b/frontend/static/tmpl/sharingItem.tmpl similarity index 100% rename from src/static/tmpl/sharingItem.tmpl rename to frontend/static/tmpl/sharingItem.tmpl diff --git a/src/static/tmpl/todoItem.tmpl b/frontend/static/tmpl/todoItem.tmpl similarity index 100% rename from src/static/tmpl/todoItem.tmpl rename to frontend/static/tmpl/todoItem.tmpl diff --git a/src/static/tmpl/tokenItem.tmpl b/frontend/static/tmpl/tokenItem.tmpl similarity index 100% rename from src/static/tmpl/tokenItem.tmpl rename to frontend/static/tmpl/tokenItem.tmpl diff --git a/src/static/tmpl/userItem.tmpl b/frontend/static/tmpl/userItem.tmpl similarity index 100% rename from src/static/tmpl/userItem.tmpl rename to frontend/static/tmpl/userItem.tmpl diff --git a/src/templates/admin.html b/frontend/templates/admin.html similarity index 100% rename from src/templates/admin.html rename to frontend/templates/admin.html diff --git a/src/templates/calendar.html b/frontend/templates/calendar.html similarity index 100% rename from src/templates/calendar.html rename to frontend/templates/calendar.html diff --git a/src/templates/collection.html b/frontend/templates/collection.html similarity index 100% rename from src/templates/collection.html rename to frontend/templates/collection.html diff --git a/src/templates/event.html b/frontend/templates/event.html similarity index 100% rename from src/templates/event.html rename to frontend/templates/event.html diff --git a/src/templates/home.html b/frontend/templates/home.html similarity index 100% rename from src/templates/home.html rename to frontend/templates/home.html diff --git a/src/templates/login.html b/frontend/templates/login.html similarity index 100% rename from src/templates/login.html rename to frontend/templates/login.html diff --git a/src/templates/todo.html b/frontend/templates/todo.html similarity index 100% rename from src/templates/todo.html rename to frontend/templates/todo.html diff --git a/tools/dial_plate_gen.py b/scripts/dial_plate_gen.py similarity index 100% rename from tools/dial_plate_gen.py rename to scripts/dial_plate_gen.py diff --git a/src/coconut-leaf.py b/src/coconut-leaf.py deleted file mode 100644 index 49531c7..0000000 --- a/src/coconut-leaf.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import sys -import getopt -import server -import config -import utils -import database - -def GetUsernamePassword(): - print('What is the first username of this calendar system?') - cache = input() - while(not utils.IsValidUsername(cache)): - print('Sorry, invalid data. Please try again.') - cache = input() - username = cache - - print("Input this user password:") - cache = input() - while(not utils.IsValidPassword(cache)): - print('Sorry, invalid data. Please try again.') - cache = input() - password = cache - - return (username, password) - -print('Coconut-leaf') -print('A self-host, multi-account calendar system.') -print('Project: https://github.com/yyc12345/coconut-leaf') -print('===================') - -# process args -# preset init value -need_init = False -try: - opts, args = getopt.getopt(sys.argv[1:], "hi") -except getopt.GetoptError: - print('Wrong arguments!') - print('python coconut-leaf.py [-i] [-h]') - sys.exit(1) -for opt, arg in opts: - if opt == '-h': - print('python coconut-leaf.py [-i]') - sys.exit(0) - elif opt == '-i': - need_init = True - -if need_init: - gotten_data = GetUsernamePassword() - calendar = database.CalendarDatabase() - calendar.init(*gotten_data) - calendar.close() - -print('Staring server...') -server.run() \ No newline at end of file diff --git a/src/config.py b/src/config.py deleted file mode 100644 index 1ab52b8..0000000 --- a/src/config.py +++ /dev/null @@ -1,7 +0,0 @@ -import json - -CustomConfig = None - -# read cfg -with open('config.cfg', 'r', encoding='utf-8') as f: - CustomConfig = json.load(f)