Skip to content

Commit e69b61b

Browse files
committed
Upgrade the /evocar_magx to use the created schedule. and added tests
1 parent fd5392e commit e69b61b

File tree

6 files changed

+146
-24
lines changed

6 files changed

+146
-24
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pip install -e .
3030

3131
y estas listo para trabajar.
3232

33+
`pip install freezegun` para correr los tests.
34+
3335
### Python 3.12
3436

3537
`pip install setuptools`
@@ -62,9 +64,8 @@ En este momento ya se puede hablar con el bot. ¿Qué le digo?
6264

6365
* `/su <password>` para reclamar permisos de admin, reemplazando `<password>` por la contraseña que hayamos
6466
elegido en la envvar `PYCAMP_BOT_MASTER_KEY`
65-
* `/agregar_pycamp <pycamp_name>` para crear un pycamp en la deb
67+
* `/empezar_pycamp <pycamp_name>` inicia el flujo de creación de un pycamp. Lo carga en la db, pide fecha de inicio y duración. Lo deja activo.
6668
* `/activar_pycamp <pycamp_name>` activa un pycamp
67-
* `/empezar_pycamp` setea la fecha de inicio del pycamp activo
6869
* `/empezar_carga_proyectos` habilita la carga de los proyectos. En este punto los pycampistas pueden cargar sus proyectos,
6970
enviandole al bot el comando `/cargar_proyecto`
7071
* `/terminar_carga_proyectos` termina carga proyectos
@@ -76,8 +77,20 @@ Para generar el schedule:
7677
* `/cronogramear` te va a preguntar cuantos dias queres cronogramear y cuantos slots por dia tenes y hacer el cronograma.
7778
* `/cambiar_slot` toma un nombre de proyecto y un slot; y te cambia ese proyecto a ese slot.
7879

80+
Para agendar los magos:
81+
82+
1. Todos los candidatos tienen que haberse registrado con `/ser_magx`
83+
2. Tiene que estar creado el schedule de presentaciones de proyectos (`/cronogramear`)
84+
85+
* `/agendar_magos` Asigna un mago por hora durante todo el PyCamp.
86+
* De 9 a 13 y de 14 a 19.
87+
* El primer día arranca después del almuerzo (14hs).
88+
* El último día termina al almuerzo (13hs).
89+
7990
### Flujo pycampista
8091

8192
* `/cargar_proyecto` carga un proyecto (si está habilitada la carga)
8293
* `/votar` envia opciones para votar (si está habilitada la votacion)
8394
* `/ver_cronograma` te muestra el cronograma!
95+
* `/ser_magx` te registra como mago.
96+
* `/evocar_magx` llama al mago de turno para pedirle ayuda.

src/pycamp_bot/commands/manage_pycamp.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,6 @@ async def cancel(update, context):
169169
)
170170

171171

172-
173-
174-
175-
176172
@active_needed
177173
@admin_needed
178174
async def end_pycamp(update, context):
@@ -183,6 +179,7 @@ async def end_pycamp(update, context):
183179
date = datetime.datetime.now()
184180

185181
is_active, pycamp = get_active_pycamp()
182+
pycamp.active = False
186183
pycamp.end = date
187184
pycamp.save()
188185

src/pycamp_bot/commands/wizard.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,31 @@ async def become_wizard(update, context):
109109

110110
await context.bot.send_message(
111111
chat_id=update.message.chat_id,
112-
text="Felicidades! Eres el Magx de turno"
112+
text="¡Felicidades! Has sido registrado como magx."
113113
)
114114

115115

116116
async def summon_wizard(update, context):
117-
username = update.message.from_user.username
118-
wizard_list = list(Pycampista.select().where(Pycampista.wizard==True))
119-
if len(wizard_list) == 0:
117+
_, pycamp = get_active_pycamp()
118+
wizard = pycamp.get_current_wizard()
119+
if wizard is None:
120120
await context.bot.send_message(
121121
chat_id=update.message.chat_id,
122-
text="No hay ningunx magx todavia"
122+
text="No hay ningunx magx agendado a esta hora :-("
123+
)
124+
return
125+
126+
username = update.message.from_user.username
127+
if username == wizard.username:
128+
await context.bot.send_message(
129+
chat_id=wizard.chat_id,
130+
text="🧙"
131+
)
132+
await context.bot.send_message(
133+
chat_id=wizard.chat_id,
134+
text="Checkeá tu cabeza: si no ténes el sombrero de magx ¡deberías!\n(soltá la compu)"
123135
)
124136
else:
125-
wizard = random.choice(wizard_list)
126137
await context.bot.send_message(
127138
chat_id=wizard.chat_id,
128139
text="PING PING PING MAGX! @{} te necesesita!".format(username)
@@ -133,23 +144,30 @@ async def summon_wizard(update, context):
133144
)
134145

135146

136-
@admin_needed
137-
async def schedule_wizards(update, context):
138-
_, pycamp = get_active_pycamp()
139-
logger.info(pycamp)
140-
147+
def persist_wizards_schedule_in_db(pycamp):
148+
"""
149+
Aux function to generate the wizards schedule and persist WizardAtPycamp instances in the DB.
150+
151+
"""
141152
schedule = define_wizards_schedule(pycamp)
142153

143154
for slot, wizard in schedule.items():
144155
start, end = slot
145156
WizardAtPycamp.create(
146157
pycamp=pycamp,
147158
wizard=wizard,
148-
slot_ini=start,
149-
slot_end=end
159+
init=start,
160+
end=end
150161
)
151162
logger.debug("From {} to {} the wizard is {}".format(start, end, wizard.username))
152163

164+
165+
@admin_needed
166+
async def schedule_wizards(update, context):
167+
_, pycamp = get_active_pycamp()
168+
169+
persist_wizards_schedule_in_db(pycamp)
170+
153171
# Mandar mensajes a los magos con su agenda propia
154172
msg = "Lista la agenda de magos."
155173
await context.bot.send_message(

src/pycamp_bot/models.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from datetime import datetime, timedelta
21
import peewee as pw
32

3+
from datetime import datetime, timedelta
4+
from random import choice
5+
46

57
DEFAULT_SLOT_PERIOD = 60 # Minutos
68

@@ -87,6 +89,22 @@ def set_as_only_active(self):
8789
def get_wizards(self):
8890
return Pycampista.select().where(Pycampista.wizard == 1)
8991

92+
def get_current_wizard(self):
93+
"""Return the Pycampista instance that's the currently scheduled wizard."""
94+
now = datetime.now()
95+
current_wizards = WizardAtPycamp.select().where(
96+
(WizardAtPycamp.pycamp == self) &
97+
(WizardAtPycamp.init <= now) &
98+
(WizardAtPycamp.end > now)
99+
)
100+
101+
wizard = None # Default if n_wiz == 0
102+
if current_wizards.count() >= 1:
103+
# Ready for an improbable future where we'll have many concurrent wizards ;-)
104+
wizard = choice(current_wizards).wizard
105+
106+
return wizard
107+
90108

91109
class PycampistaAtPycamp(BaseModel):
92110
'''
@@ -104,8 +122,8 @@ class WizardAtPycamp(BaseModel):
104122
'''
105123
pycamp = pw.ForeignKeyField(Pycamp)
106124
wizard = pw.ForeignKeyField(Pycampista)
107-
slot_ini = pw.DateTimeField()
108-
slot_end = pw.DateTimeField()
125+
init = pw.DateTimeField()
126+
end = pw.DateTimeField()
109127

110128

111129
class Slot(BaseModel):

test/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from peewee import SqliteDatabase
44
from telegram import Bot
55

6-
from pycamp_bot.models import Pycampista, Slot, Pycamp
6+
from pycamp_bot.models import Pycampista, Slot, Pycamp, WizardAtPycamp
77

88

99

@@ -13,7 +13,7 @@
1313
# use an in-memory SQLite for tests.
1414
test_db = SqliteDatabase(':memory:')
1515

16-
MODELS = [Pycampista, Slot, Pycamp]
16+
MODELS = [Pycampista, Slot, Pycamp, WizardAtPycamp]
1717

1818

1919
# Bind the given models to the db for the duration of wrapped block.

test/test_pycamp_model.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from datetime import datetime
2+
from freezegun import freeze_time
3+
from pycamp_bot.models import Pycamp, Pycampista, WizardAtPycamp
4+
from pycamp_bot.commands import wizard
5+
from test.conftest import use_test_database, test_db, MODELS
6+
7+
8+
# ---------------------------
9+
# Module Level Setup/TearDown
10+
# ---------------------------
11+
def setup_module(module):
12+
test_db.bind(MODELS, bind_refs=False, bind_backrefs=False)
13+
test_db.connect()
14+
15+
def teardown_module(module):
16+
# Not strictly necessary since SQLite in-memory databases only live
17+
# for the duration of the connection, and in the next step we close
18+
# the connection...but a good practice all the same.
19+
test_db.drop_tables(MODELS)
20+
21+
# Close connection to db.
22+
test_db.close()
23+
# If we wanted, we could re-bind the models to their original
24+
# database here. But for tests this is probably not necessary.
25+
26+
27+
class TestPycampGetCurrentWizard:
28+
29+
def init_pycamp(self):
30+
self.pycamp = Pycamp.create(
31+
headquarters="Narnia",
32+
init=datetime(2024, 6, 20),
33+
end=datetime(2024, 6, 24),
34+
)
35+
36+
@use_test_database
37+
@freeze_time("2024-06-21 11:30:00")
38+
def test_returns_correct_wizard_within_its_turno(self):
39+
"""Integration test using persist_wizards_schedule_in_db."""
40+
p = Pycamp.create(
41+
headquarters="Narnia",
42+
init=datetime(2024,6,20),
43+
end=datetime(2024,6,23),
44+
)
45+
pycamper = Pycampista.create(username="pepe", wizard=True)
46+
wizard.persist_wizards_schedule_in_db(p)
47+
48+
assert p.get_current_wizard() == pycamper
49+
50+
@use_test_database
51+
def test_no_scheduled_wizard_then_return_none(self):
52+
p = Pycamp.create(
53+
headquarters="Narnia"
54+
)
55+
# Wizard exists, but no time scheduled.
56+
pycamper = Pycampista.create(username="pepe", wizard=True)
57+
assert WizardAtPycamp.select().count() == 0
58+
59+
assert p.get_current_wizard() is None
60+
61+
@use_test_database
62+
@freeze_time("2024-06-20 10:30:00")
63+
def test_many_scheduled_wizard_then_return_one_of_them(self):
64+
p = Pycamp.create(
65+
headquarters="Narnia"
66+
)
67+
# Wizard exists, scheduled in the same time slot.
68+
gandalf = Pycampista.create(username="gandalf", wizard=True)
69+
merlin = Pycampista.create(username="merlin", wizard=True)
70+
ini = datetime(2024,6,20,10,0,0)
71+
end = datetime(2024,6,20,11,0,0)
72+
WizardAtPycamp.create(pycamp=p, wizard=gandalf, init=ini, end=end)
73+
WizardAtPycamp.create(pycamp=p, wizard=merlin, init=ini, end=end)
74+
75+
w = p.get_current_wizard()
76+
assert w == gandalf or w == merlin

0 commit comments

Comments
 (0)