Compare commits

...

10 Commits

Author SHA1 Message Date
Peter J. Holzer ad68bd86d3 Print summary of newly created meet 2025-01-27 21:35:55 +01:00
Peter J. Holzer a211048920 Don't insert weights
They aren't used and I don't even remember what they were for.
2025-01-27 21:34:36 +01:00
Peter J. Holzer 31bc47340d Announce status changes to all voters 2025-01-27 00:43:28 +01:00
Peter J. Holzer 44e06659b3 Merge branch 'range' 2025-01-26 22:49:22 +01:00
Peter J. Holzer be014dfb27 Allow null preferences 2025-01-26 22:48:32 +01:00
Peter J. Holzer b7ee8faf4d Log users who vote 2024-10-19 19:30:31 +02:00
Peter J. Holzer a9a778b4c3 Add links to votes 2024-10-19 19:17:21 +02:00
Peter J. Holzer ac2e4f0b62 Improve layout on narrow (e.g. mobile portrait) screens 2024-10-15 23:49:09 +02:00
Peter J. Holzer 47d318782b Merge branch 'range' 2024-10-15 01:21:09 +02:00
Peter J. Holzer b0b656dd9d Add weights 2024-10-15 01:13:53 +02:00
9 changed files with 136 additions and 17 deletions

82
announce_status_change Executable file
View File

@ -0,0 +1,82 @@
#!/usr/bin/python3
import datetime
import email.message
import smtplib
import psycopg
import config
min_distance = datetime.timedelta(minutes=60)
def simple_sum(meet_id, kind):
q = \
f"""
select k.*, sum(preference) as preference
from {kind} k join {kind}_vote v on k.id = v.{kind}
where k.meet = %s
group by k.id order by preference desc
"""
csr.execute(q, (meet_id,))
return csr.fetchall()
db = psycopg.connect(dbname=config.dbname, user=config.dbuser)
csr = db.cursor(row_factory=psycopg.rows.namedtuple_row)
csr.execute("select * from meet where active")
for meet in csr.fetchall():
csr.execute(
"""
select distinct email, short
from date_vote
join date on date_vote.date = date.id
join bod on date_vote.bod = bod.id
where meet = %s
order by email
""",
(meet.id,))
voters = csr.fetchall()
dates = simple_sum(meet.id, "date")
times = simple_sum(meet.id, "time")
places = simple_sum(meet.id, "place")
msg = "Aktueller Favorit:\n\n" \
f" {dates[0].date}, {times[0].time}\n" \
f" im {places[0].name}\n\n\n" \
"Abgestimmt haben:\n\n"
for v in voters:
msg += f" * {v.email}\n"
csr.execute(
"""
select *, now() - ts as age
from news
where meet = %s
order by ts desc
limit 1
""",
(meet.id,))
last_news = csr.fetchone()
if not last_news or last_news.content != msg and last_news.age >= min_distance:
print(msg)
csr.execute(
"""
insert into news(meet, ts, content) values(%s, now(), %s)
returning id
""",
(meet.id, msg))
new_news = csr.fetchone()
db.commit()
emsg = email.message.EmailMessage()
emsg["From"] ="noreply@hjp.at"
emsg["To"] = ", ".join(v.email for v in voters)
emsg["Subject"] = "Neuer Zwischenstand: " + meet.title
emsg["Message-ID"] = f"<meeat.{new_news.id}@hjp.at>"
if last_news:
emsg["In-Reply-To"] = f"<meeat.{last_news.id}@hjp.at>"
emsg.set_content(msg)
mta = smtplib.SMTP(host="localhost")
mta.send_message(emsg)

20
app.py
View File

@ -31,7 +31,22 @@ def home():
log.debug("session = %s", session)
if "user" not in session:
return redirect(url_for("register", target="/"))
return render_template("home.html")
csr = get_cursor()
csr.execute(
"""
select * from meet where id in (
select meet from date_vote dv join date d on dv.date = d.id where bod = %(bod_id)s
union
select meet from time_vote tv join time t on tv.time = t.id where bod = %(bod_id)s
union
select meet from place_vote pv join place p on pv.place = p.id where bod = %(bod_id)s
)
order by id desc
""",
{"bod_id": session["user"]["id"]}
)
meets = csr.fetchall()
return render_template("home.html", meets=meets)
@app.route("/register", methods=["GET", "POST"])
def register():
@ -142,6 +157,7 @@ def vote(key):
@app.post("/vote/date")
def vote_date():
log.debug("user = %s", session["user"])
log.debug("form = %s", request.form)
meet_id, preferences = get_preferences("date")
date_ids = list(preferences.keys())
@ -211,6 +227,7 @@ def result_date(meet_id):
@app.post("/vote/time")
def vote_time():
log.debug("user = %s", session["user"])
log.debug("form = %s", request.form)
meet_id, preferences = get_preferences("time")
time_ids = list(preferences.keys())
@ -278,6 +295,7 @@ def result_time(meet_id):
@app.post("/vote/place")
def vote_place():
log.debug("user = %s", session["user"])
log.debug("form = %s", request.form)
meet_id, preferences = get_preferences("place")
place_ids = list(preferences.keys())

View File

@ -2,6 +2,7 @@ table meet
column meet id serial primary key
column meet title text not null
column meet key text unique
column meet active boolean default true
table date
column date id serial primary key
@ -30,14 +31,20 @@ column bod keychange timestamptz default now()
table date_vote
column date_vote date int not null references date
column date_vote bod int not null references bod
column date_vote preference float4 not null
column date_vote preference float4
table time_vote
column time_vote time int not null references time
column time_vote bod int not null references bod
column time_vote preference float4 not null
column time_vote preference float4
table place_vote
column place_vote place int not null references place
column place_vote bod int not null references bod
column place_vote preference float4 not null
column place_vote preference float4
table news
column news id serial primary key
column news meet int references meet not null
column news ts timestamptz not null default now()
column news content text not null

View File

@ -48,3 +48,7 @@ for place in meet["places"]:
(place["name"], meet_id,)
)
db.commit()
csr.execute("select * from meet where id = %s", (meet_id,))
r = csr.fetchone()
print(r.id, r.key, r.title)

View File

@ -144,7 +144,11 @@ label {
font-size: 1.5rem;
}
.emoji {
font-size: 2rem;
font-size: clamp(1rem, 5vw, 2rem);
}
.slider {
display: flex;
}
table.matrix th {

View File

@ -1,7 +1,7 @@
{% for d in dates %}
<div class="vote-item">
<label for="date_{{d.id}}">{{ d.display or d.date }}</label>
<div>
<div class="slider">
<span class="emoji">😞</span>
<input type="range" name="date_{{d.id}}" value="{{d.preference}}">
<span class="emoji">😀</span>

View File

@ -6,14 +6,18 @@
</head>
<body>
<p>Hallo, {{ session.user.email }}!</p>
<p>
{{ session }}
</p>
<p>
{{ session.user }}
</p>
<p>
{{ session.user.1 }}
</p>
{% for meet in meets %}
{% if loop.first %}
<ul>
{% endif %}
<li><a href="{{url_for("vote", key=meet.key)}}">{{meet.title}}</a></li>
{% if loop.last %}
</ul>
{% endif %}
{% else %}
<p>(Du hast noch an keiner Abstimmung teilgenommen)</p>
{% endfor %}
</body>
</html>

View File

@ -1,7 +1,7 @@
{% for d in places %}
<div class="vote-item">
<label for="place_{{d.id}}">{{ d.name }}</label>
<div>
<div class="slider">
<span class="emoji">😞</span>
<input type="range" name="place_{{d.id}}" value="{{d.preference}}">
<span class="emoji">😀</span>

View File

@ -1,7 +1,7 @@
{% for d in times %}
<div class="vote-item">
<label for="time_{{d.id}}">{{ d.display or d.time }}</label>
<div>
<div class="slider">
<span class="emoji">😞</span>
<input type="range" name="time_{{d.id}}" value="{{d.preference}}">
<span class="emoji">😀</span>