25

Szukam najlepszego sposobu na modelowanie powtarzających się wydarzeń. Używam funkcji fullcalendar do wyświetlania zdarzeń. Przypuszczam jednak, że powtarzające się wydarzenia są najlepiej obsługiwane w systemie zaplecza szyn.Powtarzające się wydarzenia w kalendarzu - Railsy

Już przejrzałem inne pytania i istniejący przykładowy kod, ale nie znalazłem niczego, co pasowałoby.

Powinno zachowywać się podobnie do kalendarza google. Powinno być możliwe usuwanie/modyfikacja pojedynczych zdarzeń z serii wydarzeń cyklicznych. Ale zapisanie wszystkich zdarzeń z serii zdarzeń w bazie danych wydaje się nieefektywne. Ponadto powinno być możliwe tworzenie pojedynczych zdarzeń bez żadnych nawrotów.

Jaka byłaby dobra architektura modelu?

Mój model wydarzenie teraz wygląda tak (bez dodatkowych atrybutów):

# Table name: events 
# 
# id    :integer   not null, primary key 
# employee_id  :integer 
# created_at  :datetime 
# updated_at  :datetime 
# starts_at  :datetime 
# ends_at   :datetime 
# 

class Event < ActiveRecord::Base 
    attr_accessible :starts_at, :ends_at 
end 

Odpowiedz

37

Oto, jak mógłbym to modelować. Nie korzystałem zbyt wiele z Kalendarza Google, więc opieram tę funkcję na powtarzających się wydarzeniach z iCal.

Wszystkie modele powinny mieć zwykle id, created_at, updated_at properties. Wymienione są właściwości niestandardowe. Jeśli właściwość to inny model, zaimplementujesz ją jako stowarzyszenie, takie jak has_one lub belongs_to.

  • RecurrencePeriod
    • Event base_event # has_one :base_event, :class_name'Event'
    • Time end_date # może być zerowa, jeśli powtarza się zawsze
    • WeeklyRecurrence nawrót # has_one :recurrence, :as=>:recurrence
    • Array[OccurrenceOverride] zastępuje # has_many :overrides, :class_name=>'OccurrenceOverride'

W RecurrencePeriod rozpoczyna się w dniu, w którym rozpoczyna base_event. Zakładam również, że pracownik employee Event odnosi się do pracownika, który utworzył to wydarzenie. Numer RecurrencePeriod również należy do pracownika, który utworzył element base_event.

Model zależy od tego, jak elastycznie chcesz móc określać nawroty. Czy zamierzasz wesprzeć "we wtorek i czwartek co dwa tygodnie od 10 rano do 11 rano i od 2 po południu do 3 po południu" lub po prostu "powtarza co tydzień"? Oto model, który obsługuje po prostu "powtarza się co tydzień", "powtarza się co dwa tygodnie", itd .; możesz ją rozwinąć, jeśli potrzebujesz.

  • WeeklyRecurrence
    • Integer weeks_between_recurrences
    • RecurrencePeriod RECURRENCE_PERIOD # belongs_to :recurrence, :polymorphic=>true

używam polymorphic associations tutaj, ponieważ myślę, że mogą one być przydatne, jeśli chcesz więcej niż jeden typ nawrotów, takich jak WeeklyRecurrence i DailyRecurrence. Ale nie jestem pewien, czy to jest właściwy sposób modelowania tego, więc jeśli nie są, po prostu użyj has_one :weekly_recurrence i belongs_to :recurrence_period.

Biblioteka Ice cube wydaje się być przydatna do obliczania nawrotów. Jeśli powyższe WeeklyRecurrence nie jest wystarczająco wydajne, możesz po prostu zapisać obiekt kostki lodu Schedule w modelu, zastępując WeeklyRecurrence. Aby zapisać obiekt Schedule w modelu, zapisz go jako atrybut "harmonogram", wstaw serialize :schedule do definicji modelu i wygeneruj kolumnę tekstową "harmonogram" w bazie danych.

OccurrenceOverride obsługuje przypadek pojedynczej instancji edytowanego wydarzenia cyklicznego.

  • OccurrenceOverride
    • RecurrencePeriod recurrence_period_to_override # belongs_to :recurrence_period_to_override, :class_name=>'RecurrencePeriod'
    • Time original_start_time # jednoznacznie identyfikuje których nawrót w tym RecurrencePeriod zastąpić
    • Event replacement_event # has_one :replacement_event, :class_name=>'Event'; może być zerowa, jeśli nawrót został skasowany zamiast edytowany

Zamiast przechowywać każde wystąpienie zdarzenia indywidualnie, generują je tymczasowo, gdy trzeba pokazać je w widoku. W wersji RecurrencePeriod utwórz metodę generate_events_in_range(start_date, end_date), która generuje Event s, aby nie zapisywać w bazie danych, ale po prostu przekazać do widoku, aby mógł je wyświetlić.

Gdy użytkownik edytuje powtarzanie, powinien mieć możliwość modyfikacji wszystkich wystąpień, wszystkich przyszłych wystąpień lub tylko tego zdarzenia Jeśli zmodyfikują wszystkie wystąpienia, zmodyfikuj element base_event RecurrencePeriod. Jeśli zmodyfikują wszystkie przyszłe wystąpienia, użyj metody, którą należy wdrożyć na RecurrencePeriod, która dzieli się na dwie części RecurrencePeriod po dowolnej stronie określonej daty, a następnie zapisz zmiany tylko do drugiego okresu. Jeśli modyfikują tylko to zdarzenie, utwórz OccurrenceOverride na czas, który nadpisują, i zapisz zmiany w zastąpieniu_wystąpienia.

Gdy użytkownik mówi, że pewne zdarzenie powinno się teraz powtarzać co dwa tygodnie w przewidywalnej przyszłości, należy utworzyć nowe RecurrencePeriod z tym zdarzeniem jako element base_event i nil data_końcową. Jego powtarzanie powinno być nowe: WeeklyRecurrence z weeks_between_recurrence = 2 i nie powinno mieć żadnych s. OccurrenceOverride.

+1

Jakiekolwiek problemy z doświadczeniem, które nie spowodowały wystąpienia zdarzenia, chociaż nie można uzyskać do niego dostępu za pomocą identyfikatora? (wydajność, złożoność itp.) – ted

+0

@ted Dobre pytanie. Nigdy tego nie wdrożyłem, więc obawiam się, że nie wiem, czy generowanie zdarzeń w razie potrzeby powoduje problemy. Przypuszczam, że w razie potrzeby można skonfigurować pamięć podręczną dla wygenerowanych wystąpień. System buforowania może wykorzystywać fakt, że wygenerowane wystąpienie do zapisania jest konceptualnie bardzo podobne do "WystępowaniaOverride", choć nie jest tożsame. –

3

tylko opinia od szczytu głowy, może comenters wskaże problem nie myślę o w tej chwili:

Chciałbym zrobić model RecurringEvent (lub cokolwiek chcesz to nazwać), że has_many :events.

Załóżmy, że każde zdarzenie jest tworzone przez pracownika (na podstawie Twoich notatek), a następnie RecurringEvent będzie również belong_to :employee. Następnie można zbudować relację has_many :through, w której pracownik ma wiele wydarzeń i ma wiele powtarzających się wydarzeń.

Model RecurringEvent może mieć datę początkową i wzór, a początkowo może wykorzystywać ten wzór do tworzenia poszczególnych zdarzeń. Następnie w dowolnym zdarzeniu, które jest częścią serii cyklicznej, możesz zmodyfikować lub usunąć to pojedyncze zdarzenie, ale możesz także "zregenerować serię", usuwając wszystkie zdarzenia z serii (lub wszystkie przyszłe wydarzenia w serii) i odbudowując je na podstawie nowy wzorzec, więc na przykład przenieś spotkanie z "każdego wtorku" na "co czwartek".

Jedną z innych fajnych rzeczy jest to, że możesz stworzyć listę powtarzających się wydarzeń, które mogą dać ci dobry wgląd w główne obowiązki ludzi.

Tak jak powiedziałem, ze szczytu mojej głowy w ten sposób podchodzę do tego, ale to tylko pomysł i nie zbudowałem czegoś takiego, więc nie wiem, czy są jakieś wielkie gotyki w podejściu sugeruję.

Życzymy powodzenia, opublikuj, co robisz!

5

W moim przypadku zrobiłem coś takiego:

# Holds most of my event's data; name, description, price ... 
class Event < ActiveRecord::Base 
    has_many :schedules 
    has_many :occurrences 
    attr_accessible :started_at, :expired_at # expired_at is optional 
end 

# Holds my schedule object 
class Schedule < ActiveRecord::Base 
    belongs_to :event 
    attr_accessible :ice_cube_rule # which returns my deserialized ice_cube object 
end 

# Holds generated or manually created event occurrences 
class Occurrence < ActiveRecord::Base 
    belongs_to :event 
    attr_accessible :started_at, :expired_at 
    attr_accessible :generated # helps me tell which occurrences are out of an ice_cube generated serie 
    attr_accessible :canceled_at 
end 

Stamtąd użyłem ice_cube do zarządzania zdarzeniami i obliczenia przechowywane wyniki w tabeli wystąpień. Najpierw próbowałem pracować bez modelu zdarzenia, ale niezależnie od zaawansowania mechanizmu reguł, zawsze będziesz miał wyjątki, więc przechowywanie wystąpień w ich własnym modelu daje ci elastyczność.

Posiadanie modelu zdarzenia powoduje, że wyświetlanie zdarzeń w kalendarzu lub przy użyciu filtrów wyszukiwania daty jest o wiele łatwiejsze, ponieważ trzeba tylko wyszukiwać zdarzenia, a następnie wyświetlać dane powiązanych zdarzeń zamiast zbierać wszystkie zdarzenia z danej daty. zakres i następnie odfiltrowanie zdarzeń, w których harmonogramy nie pasują do siebie.

także możesz zgłosić zdarzenie zdarzenie jako anulowana lub zmodyfikować go (ustawienie wygenerowany atrybut na false, więc nie ma się umyć podczas edycji harmonogramu ice_cube ... lub niezależnie od potrzeby działalności jest)

Of Oczywiście, jeśli masz wydarzenia, które powtarzają się w nieskończoność, będziesz chciał ograniczyć, jak daleko w przyszłości chcesz generować te zdarzenia i używać automatycznych zadań rake'owych do czyszczenia starych i generowania zdarzeń na następny rok.

Do tej pory ten wzór działa całkiem dobrze dla mnie.

Zobacz także klejnot recurring_select, który jest całkiem schludnym wkładem do formy ice_cube.

+2

Nice post. Ciekawi Cię swoimi myślami dzięki temu podejściu: http://blog.plataformatec.com.br/2010/04/recurring-events Wygląda podobnie do Twojego. – Bruno

+0

Bruno, podejście, o którym wspomniałeś, jest ważne, dopóki nie musisz utrzymywać stanu na tych nawrotach lub radzić sobie z wyjątkami (np. Reprezentacja koncertowa zepchnięta na następny dzień w przypadku deszczu) – Jim

+0

@ Jim Czy masz demo aplikacja lub przykładowa aplikacja, którą mogę zepsuć? Podoba mi się rozwiązanie – Frank004

-1

Jestem całkiem nowy dla Rails, twoje rozwiązanie brzmi interesująco. Aby utworzyć harmonogram i powiązane wystąpienia, używasz callbacków warunkowych w Modelu zdarzeń?

W moim przypadku użytkownicy będą mogli tworzyć wydarzenia, powtarzające się co tydzień lub nie. Tak więc myślałem o powtarzającym się polu boolowskim w modelu zdarzeń. Więc myślę, że masz pierwszy oddzwanianie do tworzenia harmonogramu:

before_save :create_weekly_schedule, if: :recurring 

iw zasadzie jeden do drugiego utworzyć wystąpień:

after_save :create_occurences_if_recurring 

def create_occurences_if_recurring 
    schedules.each do |sched| 
    occurences.create(start_date: sched.start_time, end_date: sched.end_time) 
    end 
end 

Czy ten dźwięk z logicznego rozwiązania? Thx

+1

Widzę, że odpowiadasz na [moja odpowiedź] (http://stackoverflow.com/a/10266450/578288). Kiedy odpowiadasz na konkretną odpowiedź, powinieneś opublikować komentarz dotyczący tej odpowiedzi, inaczej autor nie zostanie powiadomiony. Ponadto, jeśli twoje pytanie jest długie (jak ten), nie umieszczaj go w innej odpowiedzi na oryginalne pytanie; utwórz nowe pytanie na stronie, które zawiera link do odpowiedzi, na którą odpowiadasz. –

+0

Jeśli chodzi o to, co myślę o twoim rozwiązaniu, widzę z tym problem. Mówisz 'profiles.each' - ale jeśli harmonogram jest po prostu" powtarza się co tydzień ", to pętla' each' będzie trwać wiecznie, ponieważ harmonogram nie określa daty końcowej. Dlatego nie generowałem wszystkich wydarzeń z wyprzedzeniem - jest ich nieskończona liczba. –

Powiązane problemy