feat(assessment): auto comprehensive assessment - rules engine from college scoring standard, per-semester stats, manual overrides, word export

This commit is contained in:
root
2026-10-17 15:40:07 +08:00
parent a0e2ded426
commit 49d8400722
2395 changed files with 1306906 additions and 94 deletions
@@ -0,0 +1,57 @@
# pysqlite2/__init__.py: the pysqlite2 package.
#
# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
#
# This file is part of pysqlite.
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
"""
The sqlite3 extension module provides a DB-API 2.0 (PEP 249) compliant
interface to the SQLite library, and requires SQLite 3.15.2 or newer.
To use the module, start by creating a database Connection object:
import sqlite3
cx = sqlite3.connect("test.db") # test.db will be created or opened
The special path name ":memory:" can be provided to connect to a transient
in-memory database:
cx = sqlite3.connect(":memory:") # connect to a database in RAM
Once a connection has been established, create a Cursor object and call
its execute() method to perform SQL queries:
cu = cx.cursor()
# create a table
cu.execute("create table lang(name, first_appeared)")
# insert values into a table
cu.execute("insert into lang values (?, ?)", ("C", 1972))
# execute a query and iterate over the result
for row in cu.execute("select * from lang"):
print(row)
cx.close()
The sqlite3 module is written by Gerhard Häring <gh@ghaering.de>.
"""
from sqlite3.dbapi2 import *
@@ -0,0 +1,156 @@
"""A simple SQLite CLI for the sqlite3 module.
Apart from using 'argparse' for the command-line interface,
this module implements the REPL as a thin wrapper around
the InteractiveConsole class from the 'code' stdlib module.
"""
import sqlite3
import sys
from argparse import ArgumentParser
from code import InteractiveConsole
from textwrap import dedent
from _colorize import get_theme, theme_no_color
from ._completer import completer
def execute(c, sql, suppress_errors=True, theme=theme_no_color):
"""Helper that wraps execution of SQL code.
This is used both by the REPL and by direct execution from the CLI.
'c' may be a cursor or a connection.
'sql' is the SQL string to execute.
"""
try:
for row in c.execute(sql):
print(row)
except sqlite3.Error as e:
t = theme.traceback
tp = type(e).__name__
try:
tp += f" ({e.sqlite_errorname})"
except AttributeError:
pass
print(
f"{t.type}{tp}{t.reset}: {t.message}{e}{t.reset}", file=sys.stderr
)
if not suppress_errors:
sys.exit(1)
class SqliteInteractiveConsole(InteractiveConsole):
"""A simple SQLite REPL."""
def __init__(self, connection, use_color=False):
super().__init__()
self._con = connection
self._cur = connection.cursor()
self._use_color = use_color
def runsource(self, source, filename="<input>", symbol="single"):
"""Override runsource, the core of the InteractiveConsole REPL.
Return True if more input is needed; buffering is done automatically.
Return False if input is a complete statement ready for execution.
"""
theme = get_theme(force_no_color=not self._use_color)
if not source or source.isspace():
return False
# Remember to update CLI_COMMANDS in _completer.py
if source[0] == ".":
match source[1:].strip():
case "version":
print(sqlite3.sqlite_version)
case "help":
t = theme.syntax
print(f"Enter SQL code or one of the below commands, and press enter.\n\n"
f"{t.builtin}.version{t.reset} Print underlying SQLite library version\n"
f"{t.builtin}.help{t.reset} Print this help message\n"
f"{t.builtin}.quit{t.reset} Exit the CLI, equivalent to CTRL-D\n")
case "quit":
sys.exit(0)
case "":
pass
case _ as unknown:
t = theme.traceback
self.write(f'{t.type}Error{t.reset}: {t.message}unknown '
f'command: "{unknown}"{t.reset}\n')
else:
if not sqlite3.complete_statement(source):
return True
execute(self._cur, source, theme=theme)
return False
def main(*args):
parser = ArgumentParser(description="Python sqlite3 CLI")
parser.add_argument(
"filename", type=str, default=":memory:", nargs="?",
help=(
"SQLite database to open (defaults to `:memory:`). "
"A new database is created if the file does not previously exist."
),
)
parser.add_argument(
"sql", type=str, nargs="?",
help=(
"An SQL query to execute. "
"Any returned rows are printed to `stdout`."
),
)
parser.add_argument(
"-v", "--version", action="version",
version=f"SQLite version {sqlite3.sqlite_version}",
help="Print underlying SQLite library version",
)
args = parser.parse_args(*args)
if args.filename == ":memory:":
db_name = "a transient in-memory database"
else:
db_name = repr(args.filename)
# Prepare REPL banner and prompts.
if sys.platform == "win32" and "idlelib.run" not in sys.modules:
eofkey = "CTRL-Z"
else:
eofkey = "CTRL-D"
banner = dedent(f"""
sqlite3 shell, running on SQLite version {sqlite3.sqlite_version}
Connected to {db_name}
Each command will be run using execute() on the cursor.
Type ".help" for more information; type ".quit" or {eofkey} to quit.
""").strip()
theme = get_theme()
s = theme.syntax
# Use RL_PROMPT_START_IGNORE (\001) and RL_PROMPT_END_IGNORE (\002) to
# bracket non-printing characters. This tells readline to ignore them
# when calculating screen space for redisplay during history scrolling.
sys.ps1 = f"\001{s.prompt}\002sqlite> \001{s.reset}\002"
sys.ps2 = f"\001{s.prompt}\002 ... \001{s.reset}\002"
con = sqlite3.connect(args.filename, isolation_level=None)
try:
if args.sql:
# SQL statement provided on the command-line; execute it directly.
execute(con, args.sql, suppress_errors=False, theme=theme)
else:
# No SQL provided; start the REPL.
with completer(con):
console = SqliteInteractiveConsole(con, use_color=True)
console.interact(banner, exitmsg="")
finally:
con.close()
sys.exit(0)
if __name__ == "__main__":
main(sys.argv[1:])
@@ -0,0 +1,111 @@
from _sqlite3 import OperationalError
from contextlib import contextmanager
try:
from _sqlite3 import SQLITE_KEYWORDS
except ImportError:
SQLITE_KEYWORDS = ()
CLI_COMMANDS = ('.quit', '.help', '.version')
_completion_matches = []
def _complete(con, text, state):
global _completion_matches
if state == 0:
if text.startswith("."):
_completion_matches = [
c + " " for c in CLI_COMMANDS if c.startswith(text)
]
else:
text_upper = text.upper()
_completion_matches = [
c + " " for c in SQLITE_KEYWORDS if c.startswith(text_upper)
]
cursor = con.cursor()
schemata = tuple(row[1] for row
in cursor.execute("PRAGMA database_list"))
# tables, indexes, triggers, and views
# escape '_' which can appear in attached database names
select_clauses = (
f"""\
SELECT name || ' ' FROM \"{schema}\".sqlite_master
WHERE name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'"""
for schema in schemata
)
_completion_matches.extend(
row[0]
for row in cursor.execute(
" UNION ".join(select_clauses), {"text": text}
)
)
# columns
try:
select_clauses = (
f"""\
SELECT pti.name || ' ' FROM "{schema}".sqlite_master AS sm
JOIN pragma_table_xinfo(sm.name,'{schema}') AS pti
WHERE sm.type='table' AND
pti.name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'"""
for schema in schemata
)
_completion_matches.extend(
row[0]
for row in cursor.execute(
" UNION ".join(select_clauses), {"text": text}
)
)
except OperationalError:
# skip on SQLite<3.16.0 where pragma table-valued function is
# not supported yet
pass
# functions
try:
_completion_matches.extend(
row[0] for row in cursor.execute("""\
SELECT DISTINCT UPPER(name) || '('
FROM pragma_function_list()
WHERE name NOT IN ('->', '->>') AND
name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'""",
{"text": text},
)
)
except OperationalError:
# skip on SQLite<3.30.0 where function_list is not supported yet
pass
# schemata
text_lower = text.lower()
_completion_matches.extend(c for c in schemata
if c.lower().startswith(text_lower))
_completion_matches = sorted(set(_completion_matches))
try:
return _completion_matches[state]
except IndexError:
return None
@contextmanager
def completer(con):
try:
import readline
except ImportError:
yield
return
old_completer = readline.get_completer()
def complete(text, state):
return _complete(con, text, state)
try:
readline.set_completer(complete)
if readline.backend == "editline":
# libedit uses "^I" instead of "tab"
command_string = "bind ^I rl_complete"
else:
command_string = "tab: complete"
readline.parse_and_bind(command_string)
yield
finally:
readline.set_completer(old_completer)
@@ -0,0 +1,96 @@
# pysqlite2/dbapi2.py: the DB-API 2.0 interface
#
# Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
#
# This file is part of pysqlite.
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
import datetime
import time
import collections.abc
from _sqlite3 import *
paramstyle = "qmark"
apilevel = "2.0"
Date = datetime.date
Time = datetime.time
Timestamp = datetime.datetime
def DateFromTicks(ticks):
return Date(*time.localtime(ticks)[:3])
def TimeFromTicks(ticks):
return Time(*time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
return Timestamp(*time.localtime(ticks)[:6])
sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")])
Binary = memoryview
collections.abc.Sequence.register(Row)
def register_adapters_and_converters():
from warnings import warn
msg = ("The default {what} is deprecated as of Python 3.12; "
"see the sqlite3 documentation for suggested replacement recipes")
def adapt_date(val):
warn(msg.format(what="date adapter"), DeprecationWarning, stacklevel=2)
return val.isoformat()
def adapt_datetime(val):
warn(msg.format(what="datetime adapter"), DeprecationWarning, stacklevel=2)
return val.isoformat(" ")
def convert_date(val):
warn(msg.format(what="date converter"), DeprecationWarning, stacklevel=2)
return datetime.date(*map(int, val.split(b"-")))
def convert_timestamp(val):
warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
datepart, timepart = val.split(b" ")
year, month, day = map(int, datepart.split(b"-"))
timepart_full = timepart.split(b".")
hours, minutes, seconds = map(int, timepart_full[0].split(b":"))
if len(timepart_full) == 2:
microseconds = int('{:0<6.6}'.format(timepart_full[1].decode()))
else:
microseconds = 0
val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
return val
register_adapter(datetime.date, adapt_date)
register_adapter(datetime.datetime, adapt_datetime)
register_converter("date", convert_date)
register_converter("timestamp", convert_timestamp)
register_adapters_and_converters()
# Clean up namespace
del(register_adapters_and_converters)
@@ -0,0 +1,118 @@
# Mimic the sqlite3 console shell's .dump command
# Author: Paul Kippes <kippesp@gmail.com>
# Every identifier in sql is quoted based on a comment in sqlite
# documentation "SQLite adds new keywords from time to time when it
# takes on new features. So to prevent your code from being broken by
# future enhancements, you should normally quote any identifier that
# is an English language word, even if you do not have to."
def _quote_name(name):
return '"{0}"'.format(name.replace('"', '""'))
def _escape_single_quotes(value):
return value.replace("'", "''")
def _quote_value(value):
return "'{0}'".format(_escape_single_quotes(value))
def _iterdump(connection, *, filter=None):
"""
Returns an iterator to the dump of the database in an SQL text format.
Used to produce an SQL dump of the database. Useful to save an in-memory
database for later restoration. This function should not be called
directly but instead called from the Connection method, iterdump().
"""
writeable_schema = False
cu = connection.cursor()
cu.row_factory = None # Make sure we get predictable results.
# Disable foreign key constraints, if there is any foreign key violation.
violations = cu.execute("PRAGMA foreign_key_check").fetchall()
if violations:
yield('PRAGMA foreign_keys=OFF;')
yield('BEGIN TRANSACTION;')
if filter:
# Return database objects which match the filter pattern.
filter_name_clause = 'AND "name" LIKE ?'
params = [filter]
else:
filter_name_clause = ""
params = []
# sqlite_master table contains the SQL CREATE statements for the database.
q = f"""
SELECT "name", "type", "sql"
FROM "sqlite_master"
WHERE "sql" NOT NULL AND
"type" == 'table'
{filter_name_clause}
ORDER BY "name"
"""
schema_res = cu.execute(q, params)
sqlite_sequence = []
for table_name, type, sql in schema_res.fetchall():
if table_name == 'sqlite_sequence':
rows = cu.execute('SELECT * FROM "sqlite_sequence";')
sqlite_sequence = ['DELETE FROM "sqlite_sequence"']
sqlite_sequence += [
f'INSERT INTO "sqlite_sequence" VALUES({_quote_value(table_name)},{seq_value})'
for table_name, seq_value in rows.fetchall()
]
continue
elif table_name == 'sqlite_stat1':
yield('ANALYZE "sqlite_master";')
elif table_name.startswith('sqlite_'):
continue
elif sql.startswith('CREATE VIRTUAL TABLE'):
if not writeable_schema:
writeable_schema = True
yield('PRAGMA writable_schema=ON;')
yield("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
"VALUES('table',{0},{0},0,{1});".format(
_quote_value(table_name),
_quote_value(sql),
))
else:
yield('{0};'.format(sql))
# Build the insert statement for each row of the current table
table_name_ident = _quote_name(table_name)
res = cu.execute(f'PRAGMA table_info({table_name_ident})')
column_names = [str(table_info[1]) for table_info in res.fetchall()]
q = "SELECT 'INSERT INTO {0} VALUES('{1}')' FROM {2};".format(
_escape_single_quotes(table_name_ident),
"','".join(
"||quote({0})||".format(_quote_name(col)) for col in column_names
),
table_name_ident,
)
query_res = cu.execute(q)
for row in query_res:
yield("{0};".format(row[0]))
# Now when the type is 'index', 'trigger', or 'view'
q = f"""
SELECT "name", "type", "sql"
FROM "sqlite_master"
WHERE "sql" NOT NULL AND
"type" IN ('index', 'trigger', 'view')
{filter_name_clause}
"""
schema_res = cu.execute(q, params)
for name, type, sql in schema_res.fetchall():
yield('{0};'.format(sql))
if writeable_schema:
yield('PRAGMA writable_schema=OFF;')
# gh-79009: Yield statements concerning the sqlite_sequence table at the
# end of the transaction.
for row in sqlite_sequence:
yield('{0};'.format(row))
yield('COMMIT;')