algorithm - periodo - Diseñando un sistema de calendario como Google Calendar
como calcular mi ciclo menstrual (16)
@GateKiller
No había pensado en el caso donde editas ocurrencias individuales. Tiene sentido que almacene las ocurrencias por separado en ese caso.
Cuando haces eso, sin embargo, ¿qué tan lejos en el futuro almacenas eventos? ¿Escoges una fecha arbitraria? ¿Autogenera las nuevas ocurrencias la primera vez que un usuario navega en los próximos meses / años?
Además, ¿cómo manejas el caso donde el usuario quiere editar toda la serie. "Tuvimos una reunión todos los martes por la mañana a las 10:30, pero vamos a comenzar a reunirnos el miércoles a las 8".
Tengo que crear algo similar a Google Calendar, así que creé una tabla de eventos que contiene todos los eventos para un usuario.
La parte difícil es manejar eventos recurrentes, la fila en la tabla de eventos tiene un campo tipo_evento que le dice qué tipo de evento es, ya que un evento puede ser solo para una sola fecha, O un evento repetido cada x días .
El principal desafío de diseño es manejar eventos recurrentes.
Cuando un usuario ve el calendario, usando la vista del mes, ¿cómo puedo mostrar todos los eventos para el mes dado? La consulta va a ser complicada, así que pensé que sería más fácil crear otra tabla y crear una fila para cada evento, incluidos los eventos que se repiten.
¿Qué piensan ustedes?
A partir de la experiencia anterior, crearía un nuevo registro para cada evento que ocurra y luego tendré una columna que haga referencia al evento anterior para que pueda seguir todos los eventos de una serie.
Esto tiene dos ventajas:
- No hay rutinas complicadas para resolver la próxima fecha del evento
- Las ocurrencias individuales se pueden editar sin afectar el resto
Espero que esto te de algo de reflexión :)
Creo que entiendo que su segundo párrafo significa que está considerando una segunda tabla de eventos que tiene una fila por cada ocurrencia de un evento. Yo evitaría eso.
Los eventos recurrentes deben tener una fecha de inicio y una fecha de finalización (que podría ser nula para eventos que continúan cada X días "para siempre"). Deberá decidir qué tipo de frecuencia desea permitir: cada X días, el El día n de cada mes, cada día de la semana, etc.
Probablemente tenderé hacia dos tablas: una para eventos únicos y otra para eventos recurrentes. Tendrá que consultar y mostrar los dos por separado.
Si tuviera que abordar esto (y lo intentaría lo más que pudiera para evitar reinventar esta rueda), buscaría bibliotecas de código abierto o, al menos, proyectos de código abierto con calendarios que pueda ver. . Alguna recomendación chicos?
Darren,
Así es como he diseñado mi tabla de eventos en realidad, pero cuando pienso en ello, digamos que tengo 100K usuarios que han creado eventos, esta tabla será bastante difícil, especialmente si voy a enviar correos electrónicos para recordarle a la gente su eventos (¡los eventos también pueden ser para momentos específicos del día!), ¡así que podría estar sondeando esta tabla cada 15 minutos potencialmente!
Es por eso que quería crear otra tabla que expandiría todos los eventos / eventos que se repiten, de esta manera puedo acceder a esa tabla y obtener la vista de meses de los usuarios sin hacer complicadas consultas y lógica de negocios, Y lo hará hacer encuestas mucho más eficientes también
La pregunta es, ¿esta mesa secundaria debe ser para el día o el mes siguiente? ¿Qué tiene más sentido? Como el máximo que un usuario puede ver es una vista de meses, me inclino hacia una tabla que escribe todos los eventos para un mes determinado.
(por supuesto, tendré que mantener esta tabla secundaria para cualquier edición que el usuario pueda hacer en la tabla de eventos originales).
Chan Chan,
Lo he diseñado con el mismo tipo de funcionalidad en realidad, pero solo me refiero a cómo voy a almacenar eventos, específicamente cómo manejar eventos recurrentes.
Debe tener una fecha de inicio, una fecha de finalización y una fecha de vencimiento. Los eventos de un solo día tendrían la misma fecha de inicio y fecha de finalización, y también le permitirán realizar eventos de días parciales. En cuanto a los eventos que vuelven a ocurrir, entonces la fecha de inicio y final sería para el mismo día, pero tienen diferentes tiempos, luego tiene una enumeración o tabla que especifica la frecuencia de repetición (diaria, semanal, mensual, etc.).
Esto le permite decir "este evento aparece todos los días" por día, "este evento aparece el segundo día de cada semana" por semana, "este evento aparece el día 5 de cada mes" por mes, "este evento aparece en el día 215 de cada año "por año siempre que la fecha sea inferior a la fecha de vencimiento.
Derek Park, crearía todas y cada una de las instancias de un evento en una tabla, y esta tabla se regeneraría cada mes (por lo que cualquier evento que se estableció para repetirse "para siempre" se regeneraría un mes antes usando un servicio de Windows o tal vez en el nivel de servidor sql). La encuesta no solo se realizará cada 15 minutos, sino que solo se aplicará a las encuestas relacionadas con las notificaciones por correo electrónico. Cuando alguien quiere ver sus eventos durante un mes, tendré que fetech todos sus eventos, y volver a ocurrir los eventos y descubrir qué eventos mostrar (ya que un evento que se repite pudo haber sido creado hace 6 meses, pero se relaciona con un mes que el usuario está viendo).
Zack, no estoy demasiado preocupado por tener una base de datos perfectamente normalizada, el hecho de que estoy pensando en crear una mesa secundaria ya está rompiendo una de las reglas jeje. Mis tablas de la base de datos están siguiendo ''las reglas'', pero no me importa crear tablas / columnas secundarias a veces cuando beneficia a las cosas en cuanto al rendimiento.
Intentar almacenar cada instancia de cada evento parece que sería realmente problemático y, bueno, imposible. Si alguien crea un evento que ocurre "todos los jueves, para siempre", claramente no puede almacenar todos los eventos futuros.
Podría tratar de generar los eventos futuros a pedido y rellenar los eventos futuros solo cuando sea necesario para mostrarlos o para enviar notificaciones sobre ellos. Sin embargo, si va a construir el código de generación "bajo demanda" de todos modos, ¿por qué no usarlo todo el tiempo? En lugar de salir de la tabla de eventos y luego tener que usar la generación de eventos bajo demanda para completar cualquier evento nuevo que aún no se haya agregado a la tabla, solo use la generación de eventos bajo demanda exclusivamente. El resultado final será el mismo. Con este esquema, solo necesita almacenar las fechas de inicio y finalización y la frecuencia del evento.
No veo ninguna manera de evitar la generación de eventos según demanda, por lo que no puedo ver la utilidad en la tabla de eventos. Si lo quiere por el almacenamiento en caché, entonces creo que está tomando el enfoque equivocado. En primer lugar, es un caché pobre porque de todos modos no se puede evitar la generación de eventos bajo demanda. En segundo lugar, probablemente deberías almacenar en caché a un nivel superior de todos modos. Si desea almacenar en caché, guarde en caché las páginas generadas, no los eventos.
En cuanto a hacer su encuesta más eficiente, si solo está sondeando cada 15 minutos, y su base de datos y / o servidor no puede manejar la carga, entonces ya está condenado. No hay forma de que su base de datos sea capaz de manejar a los usuarios si no puede manejar muchas, mucho más frecuentes encuestas sin perder el control.
Tengo que estar de acuerdo con @ChanChan en la lectura de las especificaciones ical de cómo almacenar estas cosas. No hay una manera fácil de manejar las recurrencias, especialmente las que tienen excepciones. Construí y reconstruí y reconstruí una base de datos para manejar esto, y sigo volviendo a ical.
No es una mala idea crear una tabla subordinada, dependiendo de los casos de uso. El algoritmo para calcular exactamente cuando ocurren. . . um, ocurrir. . . de hecho puede ser bastante complejo. No hay manera de evitar que se ejecute, pero vale la pena considerar el almacenamiento en caché de los resultados.
Yo diría que comiences con el estándar ical. Si lo usa como su modelo, entonces podrá hacer todo lo que ese calendario de google, Outlook, Macical (el programa), y obtener una integración prácticamente instantánea con ellos.
A partir de ahí, es hora de poner a punto tu ajax y javascript porque no puedes tener una interfaz web llamativa con arrastrar y soltar y varios calendarios sin una tonelada de ajax y javascript.
Así es como he diseñado mi tabla de eventos en realidad, pero cuando pienso en ello, digamos que tengo 100K usuarios que han creado eventos, esta tabla será bastante difícil, especialmente si voy a enviar correos electrónicos para recordarles a las personas su eventos (¡los eventos también pueden ser para momentos específicos del día!), ¡así que podría estar sondeando esta tabla cada 15 minutos potencialmente!
Las bases de datos hacen trabajos de excepción para manejar conjuntos de datos, por lo que no estaría demasiado preocupado por eso. Lo que debe hacer es usar esto como su tabla principal, y luego, cuando los eventos expiren, muévalos a otra tabla (como un archivo).
Lo siguiente que debe intentar es consultar el DB lo menos posible, de modo que mueva la información a un nivel de almacenamiento en caché (como velocidad ) y simplemente mantenga los datos en la base de datos.
Luego, puede dividir la información en varias bases de datos para escalar. es decir, los usuarios tienen 1-10000 calendarios en el servidor 1, 10001 - 20000 en el servidor 2, etc.
Así es como escalaría una solución como esta, pero sigo pensando que la solución original que propuse es el camino a seguir, es solo cómo se escala lo que se convierte en la pregunta.
La fuerza bruta es una forma razonable de crear una nueva fila en la tabla de eventos individuales para cada instancia del evento recurrente, y no apunta al evento que lo precede en la serie, sino al primer evento de la serie. Esto simplifica la selección y / o eliminación de todos los elementos en una serie en particular, ya que puede seleccionar en base a la identificación padre. También permite a los usuarios eliminar elementos individuales de una serie sin afectar al resto de ellos.
Esta consulta te da la serie que comienza en el elemento 3:
SELECT * FROM events WHERE id = 3 OR parentid = 3
Para obtener todos los artículos para este mes, suponiendo que tenga una fecha de inicio y una fecha de finalización en su tabla de eventos, todo lo que tendría que hacer es:
SELECT * FROM events WHERE startdate >= ''2008-08-01'' AND enddate <= ''2008-08-31''
Manejar la creación / modificación de series mediante programación no sería muy difícil, pero realmente dependería del conjunto de características que desea proporcionar y cómo cree que se utilizará. Si desea diferenciar entre series y eventos, podría tener una tabla de series separada y una serie_nulable en sus eventos, lo que le da la libertad de rebuscar con eventos individuales mientras conserva el control de su serie.
Estoy abordando exactamente este problema, y había espaciado completamente iCalendar ( rfc 2445 ) hasta leer este hilo, así que no tengo idea de qué tan bien se integrará o no con eso. De todos modos, el diseño que he encontrado hasta ahora se parece a esto:
- No es posible almacenar todas las instancias de un evento recurrente, al menos no antes de que ocurran, así que simplemente tengo una tabla que almacena la primera instancia del evento como una fecha real, una caducidad opcional y campos repeat_increment y repetibles. para describir la repetición Para instancias individuales, los campos de repisión son nulos, de lo contrario las unidades serán ''día'', ''semana'', ''mes'', ''año'' y el incremento es simplemente el múltiplo de unidades para agregar a la fecha de inicio para la próxima ocurrencia.
- Almacenar eventos pasados solo parece ventajoso si necesita establecer relaciones con otras entidades en su modelo, y aun así no es necesario tener una tabla explícita de "instancia de evento" en todos los casos. Si las otras entidades ya tienen datos de "fecha" de fecha / hora, es probable que sea suficiente una clave externa al evento (o una tabla de unión para un número de muchos a muchos).
- Para hacer "cambiar esta instancia" / "cambiar todas las instancias futuras", estaba planeando duplicar los eventos y caducar los obsoletos. Por lo tanto, para cambiar una sola instancia, caducaría la anterior en su última aparición, haría una copia para la nueva ocurrencia única con los cambios y sin ninguna repetición, y otra copia del original en la siguiente ocurrencia que se repite en el futuro. Cambiar todas las instancias futuras es similar, simplemente caducará el original y hará una nueva copia con los cambios y los detalles de la repetición.
Los dos problemas que veo con este diseño hasta ahora son:
- Hace que los eventos de tipo MWF sean difíciles de representar. Es posible, pero obliga al usuario a crear tres eventos separados que se repiten semanalmente en M, W, F de forma individual, y los cambios que deseen realizar también tendrán que hacerse en cada uno por separado. Este tipo de eventos no son particularmente útiles en mi aplicación, pero dejan una verruga en el modelo que lo hace menos universal de lo que me gustaría.
- Al copiar los eventos para hacer cambios, se rompe la asociación entre ellos, lo que podría ser útil en algunos escenarios (o, tal vez, solo ocasionalmente sería problemático). La tabla de eventos podría contener teóricamente un campo de id "copied_from" para rastrear dónde evento se originó, pero no he pensado por completo qué tan útil sería algo así. Por un lado, las relaciones jerárquicas padre / hijo son difíciles de consultar desde SQL, por lo que los beneficios deberían ser bastante pesados para compensar el costo de consultar esos datos. En su lugar, podrías usar un conjunto anidado , supongo.
Por último, creo que es posible calcular eventos para un intervalo de tiempo determinado utilizando SQL directo, pero no he resuelto los detalles exactos y creo que las consultas suelen ser demasiado engorrosas para que valgan la pena. Sin embargo, por razones de argumentación, puede usar la siguiente expresión para calcular la diferencia en meses entre el mes determinado y el año de un evento:
(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12))
Sobre la base del último ejemplo, puede usar MOD para determinar si la diferencia en meses es el múltiplo correcto:
MOD(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12), repeatIncrement) = 0
De todos modos, esto no es perfecto (no ignora los eventos caducados, no tiene en cuenta las horas de inicio / finalización del evento, etc.), por lo que solo se trata de un ejemplo motivador. En términos generales, creo que la mayoría de las consultas terminarán siendo demasiado complicadas. Probablemente sea mejor consultar los eventos que ocurren durante un rango determinado, o no caducan antes del rango, y computar las instancias en código en lugar de SQL. Si realmente desea que la base de datos realice el procesamiento, un procedimiento almacenado probablemente le facilitará la vida.
El kit de inicio Ra-Ajax Calendar presenta una muestra del manejo del evento RenderDate que puede modificar las fechas de manera específica. Aunque los "eventos recurrentes" son algo algorítmico y aquí dudo que muy pocos calendarios te ayuden mucho ...
Si alguien está haciendo Ruby, hay una gran biblioteca Runt que hace este tipo de cosas. Vale la pena echarle un vistazo. http://runt.rubyforge.org/
Como se dijo anteriormente, no reinvente la rueda , solo realce.
¡Checkout VCalendar , es de código abierto, y viene en PHP, ASP y ASP.Net (C #)!
También puede consultar Day Pilot, que ofrece un calendario escrito en Asp.Net 2.0. Ofrecen una versión lite que puede consultar, y si le funciona, puede comprar una licencia.
Actualización (9/30/09):
¡A menos, por supuesto, que la rueda esté rota! Además, puede poner una nueva capa de pintura brillante si lo desea (es decir: hacer una mejor IU). Pero al menos trate de encontrar alguna base para construir, ya que el sistema de calendario puede ser complicado (con eventos Repetidos), y se ha realizado miles de veces.
undefined escribió:
... esta mesa se golpeará bastante fuerte, especialmente si voy a enviar correos electrónicos para recordarle a la gente sobre sus eventos (¡los eventos también pueden ser para momentos específicos del día!), Así que podría sondear esta tabla cada 15 minutos !
Crea una tabla para las notificaciones. Encuesta solo.
Actualice la tabla de notificaciones cuando se actualicen eventos (recurrentes o no).
EDITAR: una vista de base de datos puede no violar los formularios normales, si eso es una preocupación. Sin embargo, es probable que desee realizar un seguimiento de las notificaciones enviadas y que aún no se han enviado a ningún lado.