Commit 2e4d6008 authored by Martin Mareš's avatar Martin Mareš
Browse files

We now store first name and last name separately

This makes things more complicated, but it allows for sorting
order compatible with the rest of the world :)
parent cbac5034
......@@ -4,7 +4,8 @@ CREATE TABLE owl_users (
uid serial PRIMARY KEY,
ukco int UNIQUE DEFAULT NULL,
auth_token varchar(64) UNIQUE DEFAULT NULL, -- this is called "access key" in UI
full_name varchar(255) NOT NULL,
first_name varchar(255) NOT NULL COLLATE "cs_CZ",
last_name varchar(255) NOT NULL COLLATE "cs_CZ",
email varchar(255) DEFAULT NULL,
created timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_admin boolean NOT NULL DEFAULT false,
......@@ -14,7 +15,7 @@ CREATE TABLE owl_users (
);
-- uid=-1 means "everybody"
INSERT INTO owl_users(uid, full_name) VALUES (-1, 'Everybody');
INSERT INTO owl_users(uid, full_name, first_name, last_name) VALUES (-1, 'Everybody', 'Every', 'Body');
CREATE TABLE owl_semesters (
semid serial PRIMARY KEY,
......
......@@ -294,6 +294,10 @@ def must_be_admin():
return None
def full_name(u):
return u.first_name + ' ' + u.last_name
@app.route('/c/<sident>/<cident>/')
def course_index(sident, cident):
err = course_init(sident, cident)
......@@ -433,7 +437,7 @@ def topic_index(sident, cident, tident, student_uid=None):
flash('Post not submitted, see below for errors.', 'error')
db_query("""
SELECT p.pid, p.target_uid, p.author_uid, u.full_name AS author_name, p.created, p.modified, p.comment, p.attachment, p.points
SELECT p.pid, p.target_uid, p.author_uid, CONCAT(u.first_name, ' ', u.last_name) AS author_name, p.created, p.modified, p.comment, p.attachment, p.points
FROM owl_posts p
LEFT JOIN owl_users u ON u.uid = p.author_uid
WHERE tid=%s
......@@ -563,12 +567,12 @@ def topic_student_grade(sident=None, cident=None, tident=None):
return "Only graders can grade", HTTPStatus.FORBIDDEN
db_query("""
SELECT s.uid, s.full_name
SELECT s.uid, s.first_name, s.last_name
FROM owl_enroll e
JOIN owl_users s ON s.uid = e.uid
WHERE e.cid = %s
AND e.is_teacher = false
ORDER BY s.full_name
ORDER BY s.last_name, s.first_name
""", (g.course.cid,))
students = {}
......@@ -776,13 +780,13 @@ def teacher(sident, cident=None):
topics_followed_by_header[cid][tid] = True
db_query("""
SELECT c.cid, s.uid, s.full_name, s.email
SELECT c.cid, s.uid, s.first_name, s.last_name, s.email
FROM owl_courses c
JOIN owl_enroll e ON e.cid = c.cid
JOIN owl_users s ON s.uid = e.uid
WHERE c.cid IN (SELECT cid FROM tmp_cids)
AND e.is_teacher = false
ORDER BY c.ident, s.full_name
ORDER BY c.ident, s.last_name, s.first_name
""")
students = { cid: {} for cid in courses.keys() }
......@@ -923,7 +927,7 @@ def results_csv(sident, cident):
def export_points(format):
db_query("""
SELECT u.uid, u.ukco, u.full_name
SELECT u.uid, u.ukco, u.first_name, u.last_name
FROM owl_users u
JOIN owl_enroll e USING(uid)
WHERE e.cid = %s
......@@ -965,7 +969,7 @@ def export_points(format):
out.append({
"uid": s.uid,
"ukco": s.ukco,
"name": s.full_name,
"name": full_name(s),
"tasks": points[s.uid],
"points": sum(points[s.uid].values()),
})
......@@ -982,7 +986,7 @@ def export_points(format):
writer.writerow([
s.uid,
s.ukco,
s.full_name,
full_name(s),
sum(points[s.uid].values()),
] + [points[s.uid].get(t.ident, "") for t in topics])
resp = make_response(out.getvalue())
......@@ -1106,13 +1110,13 @@ def admin_topic_graders(sident, cident, tid):
return redirect(url_for('admin_topic_graders', sident=sident, cident=cident, tid=tid))
else:
db_query("""
SELECT s.uid, s.full_name,
SELECT s.uid, s.first_name, s.last_name,
EXISTS (SELECT * FROM owl_student_graders x WHERE x.tid=%s AND x.uid=s.uid) AS is_grader
FROM owl_enroll e
JOIN owl_users s ON s.uid = e.uid
WHERE e.cid = %s
AND e.is_teacher = false
ORDER BY s.full_name
ORDER BY s.last_name, s.first_name
""", (topic.tid, g.course.cid))
students = [(s, AssignGraderForm(uid=s.uid)) for s in db.fetchall()]
......@@ -1252,7 +1256,7 @@ def admin_course(sident, cident):
return err
db_query("""
SELECT u.full_name
SELECT u.first_name, u.last_name
FROM owl_enroll e
JOIN owl_users u USING(uid)
WHERE e.cid=%s
......@@ -1260,7 +1264,7 @@ def admin_course(sident, cident):
""", (g.course.cid,))
teachers = db.fetchall()
return render_template('admin-course.html', teachers=[t.full_name for t in teachers])
return render_template('admin-course.html', teachers=[full_name(t) for t in teachers])
@app.route('/admin/courses')
......@@ -1276,14 +1280,14 @@ def admin_courses():
courses = db.fetchall()
db_query("""
SELECT e.cid, u.full_name
SELECT e.cid, u.first_name, u.last_name
FROM owl_enroll e
JOIN owl_users u USING(uid)
WHERE is_teacher = true
""")
teachers = defaultdict(list)
for e in db.fetchall():
teachers[e.cid].append(e.full_name)
teachers[e.cid].append(full_name(e))
return render_template('admin-courses.html',
courses=courses,
......@@ -1309,7 +1313,7 @@ def login():
db_query("SELECT * FROM owl_users WHERE auth_token=%s", (form.key.data,))
row = db.fetchone()
if row:
app.logger.info('Logged in user: uid=%s, cn=%s, admin=%s by key', row.uid, row.full_name, row.is_admin)
app.logger.info('Logged in user: uid=%s, cn=%s, admin=%s by key', row.uid, full_name(row), row.is_admin)
session_from_db(row)
return redirect(form.next.data or url_for('index'))
......@@ -1370,16 +1374,16 @@ def session_from_cas(user, attrs):
db_query("SELECT * FROM owl_users WHERE ukco=%s", (user,))
row = db.fetchone()
if row:
app.logger.info('Logged in user: uid=%s, ukco=%s, cn=%s, mail=%s, admin=%s', row.uid, row.ukco, row.full_name, row.email, row.is_admin)
app.logger.info('Logged in user: uid=%s, ukco=%s, cn=%s, mail=%s, admin=%s', row.uid, row.ukco, full_name(row), row.email, row.is_admin)
else:
email = primary_cas_email(attrs['mail'])
db_query("""
INSERT INTO owl_users(ukco, full_name, email)
INSERT INTO owl_users(ukco, first_name, last_name, email)
VALUES (%s, %s, %s)
RETURNING uid, ukco, full_name, email, is_admin, inline_att
""", (user, attrs['cn'], email))
RETURNING uid, ukco, first_name, last_name, email, is_admin, inline_att
""", (user, attrs['givenname'], attrs['sn'], email))
row = db.fetchone()
app.logger.info('Created new user: uid=%s, ukco=%s, cn=%s, mail=%s', row.uid, row.ukco, row.full_name, row.email)
app.logger.info('Created new user: uid=%s, ukco=%s, cn=%s, mail=%s', row.uid, row.ukco, full_name(row), row.email)
db_connection.commit()
session_from_db(row)
......@@ -1388,7 +1392,7 @@ def session_from_cas(user, attrs):
def session_from_db(row):
session['uid'] = row.uid
session['name'] = row.full_name
session['name'] = full_name(row),
if row.is_admin:
session['admin'] = 1
else:
......@@ -1503,7 +1507,7 @@ def cli_find_user(name):
if len(users) > 1:
cli_die(f'Multiple users with e-mail {name} found')
db_query("SELECT * FROM owl_users WHERE full_name=%s", (name,))
db_query("SELECT * FROM owl_users WHERE CONCAT(first_name, ' ', last_name)=%s", (name,))
users = db.fetchall()
if not users:
cli_die(f'No user with e-mail or full name {name} found')
......@@ -1584,7 +1588,7 @@ def cli_add_teacher(course_ident, teacher):
@app.cli.command("convert-emails")
def cli_convert_emails():
"""Convert up e-mail addresses in the database from old SIS format."""
"""Convert e-mail addresses in the database from old SIS format."""
db_query("""
SELECT *
......@@ -1601,6 +1605,26 @@ def cli_convert_emails():
db_connection.commit()
@app.cli.command("convert-names")
def cli_convert_emails():
"""Convert names in the database to new format."""
db_query("""
SELECT *
FROM owl_users
""")
users = db.fetchall()
for u in users:
f = u.full_name.split(' ')
if len(f) == 2:
db_query("UPDATE owl_users SET first_name=%s, last_name=%s WHERE uid=%s", (f[0], f[1], u.uid))
else:
print(f'UID {u.uid} needs manual fix: {f}')
db_connection.commit()
### Sending notifications ###
......@@ -1648,7 +1672,7 @@ def send_notify_post(post):
app.logger.info(f"Notify: pid={post.pid} cid={course.cid} tid={topic.tid} author_uid={author.uid} target_uid={post.target_uid}")
db_query("""
SELECT u.uid, u.email, u.full_name, e.is_teacher, u.notify_self
SELECT u.uid, u.email, u.first_name, u.last_name, e.is_teacher, u.notify_self
FROM owl_enroll e
JOIN owl_users u ON u.uid = e.uid
WHERE e.cid = %s
......@@ -1677,8 +1701,8 @@ def send_notify_to_dest(post, topic, course, author, target, dest):
app.logger.info(f"E-mail: uid={dest.uid} email={dest.email}")
msg = email.message.EmailMessage()
msg['From'] = email.headerregistry.Address(display_name=author.full_name + " via Owl", addr_spec=app.config['MAIL_FROM'])
msg['To'] = email.headerregistry.Address(display_name=dest.full_name, addr_spec=dest.email)
msg['From'] = email.headerregistry.Address(display_name=full_name(author) + " via Owl", addr_spec=app.config['MAIL_FROM'])
msg['To'] = email.headerregistry.Address(display_name=full_name(dest), addr_spec=dest.email)
msg['Subject'] = f'Owl: {topic.title}'
msg['Date'] = (post.modified or post.created).astimezone()
if 'MAIL_REFERENCES' in app.config:
......@@ -1687,14 +1711,14 @@ def send_notify_to_dest(post, topic, course, author, target, dest):
if dest.is_teacher and target:
target_uid = target.uid
title = f"{topic.title} / {target.full_name}"
title = f"{topic.title} / {full_name(target)}"
else:
target_uid = None
title = topic.title
h = [
('Topic', title),
('Post by', author.full_name),
('Post by', full_name(author)),
('Posted', post.created.replace(microsecond=0).astimezone().strftime("%Y-%m-%d %H:%M:%S")),
]
if post.modified:
......
......@@ -12,7 +12,7 @@
<li><form method="POST" action="">
{{ form.csrf_token }}
{{ form.uid }}
<b>{{ s.full_name }}</b>:
<b>{{ s.first_name }} {{ s.last_name }}</b>:
{{ form.unassign(class='ok') }}
</form></li>
{% endfor %}
......@@ -24,7 +24,7 @@
<li><form method="POST" action="">
{{ form.csrf_token }}
{{ form.uid }}
<b>{{ s.full_name }}</b>:
<b>{{ s.first_name }} {{ s.last_name }}</b>:
{{ form.assign(class='ok') }}
</form></li>
{% endfor %}
......
......@@ -5,7 +5,8 @@
<form method="POST" action="">
{{ form.csrf_token }}
<table class=settings>
<tr><td>Name:<td>{{ user.full_name }}
<tr><td>First name:<td>{{ user.first_name }}
<tr><td>Last name:<td>{{ user.last_name }}
<tr><td>E-mail:<td>{{ user.email }}
<tr><td>Notify by e-mail:<td>{{ form.notify() }}
<tr><td>Notify on own posts:<td>{{ form.notify_self() }}
......
......@@ -34,7 +34,7 @@
{% endfor %}
<td class=pts>{{ course_totals[c.cid] }}
{% for s in students[c.cid].values() %}
<tr><td class='tbeforehdr'>{{ s.full_name }}
<tr><td class='tbeforehdr'>{{ s.first_name }} {{ s.last_name }}
{{ s.email|mailto('✉') }}
{% for t in topics[c.cid].values() %}
{% set sol=solutions[c.cid][s.uid][t.tid] %}
......
......@@ -19,7 +19,7 @@
{% if c.anon_grading and s.uid != g.uid %}
Student {{ s.uid }}
{% else %}
{{ s.full_name }}
{{ s.first_name }} {{ s.last_name }}
{% endif %}
{% set sol=solutions[s.uid] %}
{% set cls=[] %}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment