Cleanups. Adding Speaker detail.
This commit is contained in:
parent
a447ca476f
commit
d4d7fad439
|
@ -44,6 +44,10 @@ speakerDecoder : Decoder Speaker
|
|||
speakerDecoder =
|
||||
decode Speaker
|
||||
|> required "name" string
|
||||
|> required "slug" string
|
||||
|> required "biography" string
|
||||
|> optional "large_picture_url" (nullable string) Nothing
|
||||
|> optional "small_picture_url" (nullable string) Nothing
|
||||
|
||||
|
||||
eventDecoder : Decoder Event
|
||||
|
@ -52,7 +56,7 @@ eventDecoder =
|
|||
|> required "title" string
|
||||
|> required "slug" string
|
||||
|> required "abstract" string
|
||||
|> required "speakers" (list speakerDecoder)
|
||||
|> required "speaker_slugs" (list string)
|
||||
|> required "video_recording" bool
|
||||
|> optional "video_url" string ""
|
||||
|> required "event_type" string
|
||||
|
@ -115,3 +119,4 @@ initDataDecoder =
|
|||
|> required "event_instances" (list eventInstanceDecoder)
|
||||
|> required "event_locations" (list eventLocationDecoder)
|
||||
|> required "event_types" (list eventTypeDecoder)
|
||||
|> required "speakers" (list speakerDecoder)
|
||||
|
|
|
@ -37,7 +37,7 @@ init flags location =
|
|||
Filter [] [] []
|
||||
|
||||
model =
|
||||
Model [] [] [] [] [] flags emptyFilter location currentRoute False
|
||||
Model [] [] [] [] [] [] flags emptyFilter location currentRoute False
|
||||
in
|
||||
model ! [ sendInitMessage flags.camp_slug flags.websocket_server ]
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module Messages exposing (Msg(..))
|
|||
|
||||
-- Local modules
|
||||
|
||||
import Models exposing (Day, EventType, EventLocation, EventInstance)
|
||||
import Models exposing (Day, EventType, EventLocation, EventInstance, VideoRecordingFilter)
|
||||
|
||||
|
||||
-- External modules
|
||||
|
@ -15,6 +15,6 @@ type Msg
|
|||
| WebSocketPayload String
|
||||
| ToggleEventTypeFilter EventType
|
||||
| ToggleEventLocationFilter EventLocation
|
||||
| ToggleVideoRecordingFilter { name : String, slug : String, filter : EventInstance -> Bool }
|
||||
| ToggleVideoRecordingFilter VideoRecordingFilter
|
||||
| OnLocationChange Location
|
||||
| BackInHistory
|
||||
|
|
|
@ -10,11 +10,36 @@ import Date exposing (Date, now)
|
|||
import Navigation exposing (Location)
|
||||
|
||||
|
||||
type alias EventSlug =
|
||||
String
|
||||
|
||||
|
||||
type alias EventInstanceSlug =
|
||||
String
|
||||
|
||||
|
||||
type alias SpeakerSlug =
|
||||
String
|
||||
|
||||
|
||||
type alias DaySlug =
|
||||
String
|
||||
|
||||
|
||||
type alias FilterQuery =
|
||||
String
|
||||
|
||||
|
||||
|
||||
-- Route is defined here rather than in Routing.elm due to it being used in Model. If it were in Routing.elm we would have a circular dependency.
|
||||
|
||||
|
||||
type Route
|
||||
= OverviewRoute
|
||||
| OverviewFilteredRoute String
|
||||
| DayRoute String
|
||||
| OverviewFilteredRoute FilterQuery
|
||||
| DayRoute DaySlug
|
||||
| EventRoute EventSlug
|
||||
| SpeakerRoute SpeakerSlug
|
||||
| NotFoundRoute
|
||||
|
||||
|
||||
|
@ -24,6 +49,7 @@ type alias Model =
|
|||
, eventInstances : List EventInstance
|
||||
, eventLocations : List EventLocation
|
||||
, eventTypes : List EventType
|
||||
, speakers : List Speaker
|
||||
, flags : Flags
|
||||
, filter : Filter
|
||||
, location : Location
|
||||
|
@ -35,10 +61,14 @@ type alias Model =
|
|||
type alias Filter =
|
||||
{ eventTypes : List EventType
|
||||
, eventLocations : List EventLocation
|
||||
, videoRecording : List { name : String, slug : String, filter : EventInstance -> Bool }
|
||||
, videoRecording : List VideoRecordingFilter
|
||||
}
|
||||
|
||||
|
||||
type alias VideoRecordingFilter =
|
||||
{ name : String, slug : String, filter : EventInstance -> Bool }
|
||||
|
||||
|
||||
type alias Day =
|
||||
{ day_name : String
|
||||
, date : Date
|
||||
|
@ -48,17 +78,13 @@ type alias Day =
|
|||
|
||||
type alias Speaker =
|
||||
{ name : String
|
||||
, slug : SpeakerSlug
|
||||
, biography : String
|
||||
, largePictureUrl : Maybe String
|
||||
, smallPictureUrl : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias EventSlug =
|
||||
String
|
||||
|
||||
|
||||
type alias EventInstanceSlug =
|
||||
String
|
||||
|
||||
|
||||
type alias EventInstance =
|
||||
{ title : String
|
||||
, slug : EventInstanceSlug
|
||||
|
@ -83,7 +109,7 @@ type alias Event =
|
|||
{ title : String
|
||||
, slug : EventSlug
|
||||
, abstract : String
|
||||
, speakers : List Speaker
|
||||
, speakerSlugs : List SpeakerSlug
|
||||
, videoRecording : Bool
|
||||
, videoUrl : String
|
||||
, eventType : String
|
||||
|
|
|
@ -14,16 +14,16 @@ import UrlParser exposing (Parser, (</>), oneOf, map, top, s, string, parseHash)
|
|||
{--
|
||||
URLs to support:
|
||||
|
||||
- #
|
||||
- #/
|
||||
This show the overview of the schedule
|
||||
|
||||
- #?type={types},location={locations},video={no,yes,link}
|
||||
- #/?type={types},location={locations},video={not-to-be-recorded,to-be-recorded,has-recording}
|
||||
This is the overview, just with filters enable
|
||||
|
||||
- #day/{year}-{month}-{day}
|
||||
- #/day/{year}-{month}-{day}
|
||||
Show a particular day
|
||||
|
||||
- #event/{slug}
|
||||
- #/event/{slug}
|
||||
Show a particular event
|
||||
|
||||
--}
|
||||
|
@ -36,6 +36,7 @@ matchers =
|
|||
, map OverviewFilteredRoute (top </> string)
|
||||
, map DayRoute (s "day" </> string)
|
||||
, map EventRoute (s "event" </> string)
|
||||
, map SpeakerRoute (s "speaker" </> string)
|
||||
]
|
||||
|
||||
|
||||
|
@ -43,3 +44,29 @@ parseLocation : Location -> Route
|
|||
parseLocation location =
|
||||
parseHash matchers location
|
||||
|> Maybe.withDefault NotFoundRoute
|
||||
|
||||
|
||||
routeToString : Route -> String
|
||||
routeToString route =
|
||||
let
|
||||
parts =
|
||||
case route of
|
||||
OverviewRoute ->
|
||||
[]
|
||||
|
||||
OverviewFilteredRoute query ->
|
||||
[ query ]
|
||||
|
||||
DayRoute iso ->
|
||||
[ "day", iso ]
|
||||
|
||||
EventRoute slug ->
|
||||
[ "event", slug ]
|
||||
|
||||
SpeakerRoute slug ->
|
||||
[ "speaker", slug ]
|
||||
|
||||
NotFoundRoute ->
|
||||
[]
|
||||
in
|
||||
"#/" ++ String.join "/" parts
|
||||
|
|
|
@ -7,6 +7,7 @@ import Messages exposing (Msg(..))
|
|||
import Views.DayPicker exposing (dayPicker)
|
||||
import Views.DayView exposing (dayView)
|
||||
import Views.EventDetail exposing (eventDetailView)
|
||||
import Views.SpeakerDetail exposing (speakerDetailView)
|
||||
import Views.ScheduleOverview exposing (scheduleOverviewView)
|
||||
|
||||
|
||||
|
@ -48,6 +49,9 @@ view model =
|
|||
EventRoute eventSlug ->
|
||||
eventDetailView eventSlug model
|
||||
|
||||
SpeakerRoute speakerSlug ->
|
||||
speakerDetailView speakerSlug model
|
||||
|
||||
NotFoundRoute ->
|
||||
div [] [ text "Not found!" ]
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ module Views.DayPicker exposing (..)
|
|||
|
||||
import Models exposing (..)
|
||||
import Messages exposing (Msg(..))
|
||||
import Routing exposing (routeToString)
|
||||
|
||||
|
||||
-- Core modules
|
||||
|
@ -51,7 +52,7 @@ dayPicker model =
|
|||
, ( "btn-default", not isAllDaysActive )
|
||||
, ( "btn-primary", isAllDaysActive )
|
||||
]
|
||||
, href ("#")
|
||||
, href <| routeToString OverviewRoute
|
||||
]
|
||||
[ text "All Days"
|
||||
]
|
||||
|
@ -78,7 +79,7 @@ dayButton day activeDate =
|
|||
, ( "btn-default", not isActive )
|
||||
, ( "btn-primary", isActive )
|
||||
]
|
||||
, href ("#day/" ++ (Date.Extra.toFormattedString "y-MM-dd" day.date))
|
||||
, href <| routeToString <| DayRoute <| Date.Extra.toFormattedString "y-MM-dd" day.date
|
||||
]
|
||||
[ text day.day_name
|
||||
]
|
||||
|
|
|
@ -3,7 +3,8 @@ module Views.DayView exposing (dayView)
|
|||
-- Local modules
|
||||
|
||||
import Messages exposing (Msg(..))
|
||||
import Models exposing (Model, Day, EventInstance, EventLocation)
|
||||
import Models exposing (Model, Day, EventInstance, EventLocation, Route(EventRoute))
|
||||
import Routing exposing (routeToString)
|
||||
|
||||
|
||||
-- Core modules
|
||||
|
@ -236,7 +237,7 @@ eventInstanceBlock offset numberInGroup ( eventInstance, lefts ) =
|
|||
, ( "background-color", eventInstance.backgroundColor )
|
||||
, ( "color", eventInstance.forgroundColor )
|
||||
]
|
||||
, href ("#event/" ++ eventInstance.eventSlug)
|
||||
, href <| routeToString <| EventRoute eventInstance.eventSlug
|
||||
]
|
||||
[ p [] [ text ((Date.Extra.toFormattedString "HH:mm" eventInstance.from) ++ " " ++ eventInstance.title) ]
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ module Views.EventDetail exposing (eventDetailView)
|
|||
|
||||
import Messages exposing (Msg(..))
|
||||
import Models exposing (..)
|
||||
import Routing exposing (routeToString)
|
||||
|
||||
|
||||
-- Core modules
|
||||
|
@ -27,15 +28,12 @@ eventDetailView eventSlug model =
|
|||
model.events
|
||||
|> List.filter (\e -> e.slug == eventSlug)
|
||||
|> List.head
|
||||
|
||||
eventInstances =
|
||||
List.filter (\instance -> instance.eventSlug == eventSlug) model.eventInstances
|
||||
in
|
||||
case event of
|
||||
Just event ->
|
||||
div [ class "row" ]
|
||||
[ eventDetailContent event
|
||||
, eventDetailSidebar event eventInstances
|
||||
, eventDetailSidebar event model
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
|
@ -53,12 +51,52 @@ eventDetailContent event =
|
|||
, text " Back"
|
||||
]
|
||||
, h3 [] [ text event.title ]
|
||||
, p [] [ Markdown.toHtml [] event.abstract ]
|
||||
, div [] [ Markdown.toHtml [] event.abstract ]
|
||||
]
|
||||
|
||||
|
||||
eventDetailSidebar : Event -> List EventInstance -> Html Msg
|
||||
eventDetailSidebar event eventInstances =
|
||||
getSpeakersFromSlugs : List Speaker -> List SpeakerSlug -> List Speaker -> List Speaker
|
||||
getSpeakersFromSlugs speakers slugs collectedSpeakers =
|
||||
case speakers of
|
||||
[] ->
|
||||
collectedSpeakers
|
||||
|
||||
speaker :: rest ->
|
||||
let
|
||||
foundSlug =
|
||||
slugs
|
||||
|> List.filter (\slug -> slug == speaker.slug)
|
||||
|> List.head
|
||||
|
||||
foundSpeaker =
|
||||
case foundSlug of
|
||||
Just slug ->
|
||||
[ speaker ]
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
|
||||
newSlugs =
|
||||
case foundSlug of
|
||||
Just slug ->
|
||||
List.filter (\x -> x /= slug) slugs
|
||||
|
||||
Nothing ->
|
||||
slugs
|
||||
|
||||
newCollectedSpeakers =
|
||||
collectedSpeakers ++ foundSpeaker
|
||||
in
|
||||
case slugs of
|
||||
[] ->
|
||||
collectedSpeakers
|
||||
|
||||
_ ->
|
||||
getSpeakersFromSlugs rest newSlugs newCollectedSpeakers
|
||||
|
||||
|
||||
eventDetailSidebar : Event -> Model -> Html Msg
|
||||
eventDetailSidebar event model =
|
||||
let
|
||||
videoRecordingLink =
|
||||
case event.videoUrl of
|
||||
|
@ -71,6 +109,12 @@ eventDetailSidebar event eventInstances =
|
|||
, text " Watch recording here!"
|
||||
]
|
||||
]
|
||||
|
||||
eventInstances =
|
||||
List.filter (\instance -> instance.eventSlug == event.slug) model.eventInstances
|
||||
|
||||
speakers =
|
||||
getSpeakersFromSlugs model.speakers event.speakerSlugs []
|
||||
in
|
||||
div
|
||||
[ classList
|
||||
|
@ -80,7 +124,7 @@ eventDetailSidebar event eventInstances =
|
|||
]
|
||||
]
|
||||
(videoRecordingLink
|
||||
++ [ speakerSidebar event.speakers
|
||||
++ [ speakerSidebar speakers
|
||||
, eventMetaDataSidebar event
|
||||
, eventInstancesSidebar eventInstances
|
||||
]
|
||||
|
@ -121,7 +165,7 @@ speakerSidebar speakers =
|
|||
speakerDetail : Speaker -> Html Msg
|
||||
speakerDetail speaker =
|
||||
li []
|
||||
[ text speaker.name
|
||||
[ a [ href <| routeToString <| SpeakerRoute speaker.slug ] [ text speaker.name ]
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ module Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQu
|
|||
-- Local modules
|
||||
|
||||
import Messages exposing (Msg(..))
|
||||
import Models exposing (Model, EventInstance, Filter, Day)
|
||||
import Models exposing (Model, EventInstance, Filter, Day, FilterQuery, Route(OverviewFilteredRoute), VideoRecordingFilter)
|
||||
import Routing exposing (routeToString)
|
||||
|
||||
|
||||
-- Core modules
|
||||
|
@ -14,7 +15,7 @@ import Regex
|
|||
-- External modules
|
||||
|
||||
import Html exposing (Html, text, div, ul, li, span, i, h4)
|
||||
import Html.Attributes exposing (class, classList, href)
|
||||
import Html.Attributes exposing (class, classList)
|
||||
import Html.Events exposing (onClick)
|
||||
import Date.Extra exposing (Interval(..), equalBy)
|
||||
|
||||
|
@ -95,7 +96,7 @@ hasRecordingFilter eventInstance =
|
|||
eventInstance.videoUrl /= ""
|
||||
|
||||
|
||||
videoRecordingFilters : List { name : String, slug : String, filter : EventInstance -> Bool }
|
||||
videoRecordingFilters : List VideoRecordingFilter
|
||||
videoRecordingFilters =
|
||||
[ { name = "Will not be recorded", slug = "not-to-be-recorded", filter = notRecordedFilter }
|
||||
, { name = "Will recorded", slug = "to-be-recorded", filter = recordedFilter }
|
||||
|
@ -177,7 +178,7 @@ getFilter filterType modelItems query =
|
|||
List.filterMap (\x -> findFilter modelItems x) filterSlugs
|
||||
|
||||
|
||||
parseFilterFromQuery : String -> Model -> Filter
|
||||
parseFilterFromQuery : FilterQuery -> Model -> Filter
|
||||
parseFilterFromQuery query model =
|
||||
let
|
||||
types =
|
||||
|
@ -195,7 +196,7 @@ parseFilterFromQuery query model =
|
|||
}
|
||||
|
||||
|
||||
filterToQuery : Filter -> String
|
||||
filterToQuery : Filter -> FilterQuery
|
||||
filterToQuery filter =
|
||||
let
|
||||
typePart =
|
||||
|
@ -225,4 +226,4 @@ filterToQuery filter =
|
|||
result =
|
||||
String.join "&" (List.filter (\x -> x /= "") [ typePart, locationPart, videoPart ])
|
||||
in
|
||||
"#" ++ result
|
||||
routeToString <| OverviewFilteredRoute result
|
||||
|
|
|
@ -3,8 +3,9 @@ module Views.ScheduleOverview exposing (scheduleOverviewView)
|
|||
-- Local modules
|
||||
|
||||
import Messages exposing (Msg(..))
|
||||
import Models exposing (Model, Day, EventInstance, Filter)
|
||||
import Models exposing (Model, Day, EventInstance, Filter, Route(EventRoute))
|
||||
import Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQuery)
|
||||
import Routing exposing (routeToString)
|
||||
|
||||
|
||||
-- External modules
|
||||
|
@ -50,7 +51,7 @@ dayEventInstanceView eventInstance =
|
|||
[ ( "event", True )
|
||||
, ( "event-in-overview", True )
|
||||
]
|
||||
, href ("#event/" ++ eventInstance.eventSlug)
|
||||
, href <| routeToString <| EventRoute eventInstance.eventSlug
|
||||
, style
|
||||
[ ( "background-color", eventInstance.backgroundColor )
|
||||
, ( "color", eventInstance.forgroundColor )
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from channels.generic.websockets import JsonWebsocketConsumer
|
||||
|
||||
from camps.models import Camp
|
||||
from .models import Event, EventInstance, Favorite, EventLocation, EventType
|
||||
from .models import Event, EventInstance, Favorite, EventLocation, EventType, Speaker
|
||||
|
||||
|
||||
class ScheduleConsumer(JsonWebsocketConsumer):
|
||||
|
@ -41,12 +41,16 @@ class ScheduleConsumer(JsonWebsocketConsumer):
|
|||
event_types_query_set = EventType.objects.filter()
|
||||
event_types = list([x.serialize() for x in event_types_query_set])
|
||||
|
||||
speakers_query_set = Speaker.objects.filter(camp=camp)
|
||||
speakers = list([x.serialize() for x in speakers_query_set])
|
||||
|
||||
data = {
|
||||
"action": "init",
|
||||
"events": events,
|
||||
"event_instances": event_instances,
|
||||
"event_locations": event_locations,
|
||||
"event_types": event_types,
|
||||
"speakers": speakers,
|
||||
"days": days,
|
||||
}
|
||||
except Camp.DoesNotExist:
|
||||
|
|
|
@ -14,6 +14,8 @@ from django.dispatch import receiver
|
|||
from django.utils.text import slugify
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.urls import reverse
|
||||
from django.apps import apps
|
||||
|
@ -433,8 +435,8 @@ class Event(CampRelatedModel):
|
|||
'title': self.title,
|
||||
'slug': self.slug,
|
||||
'abstract': self.abstract,
|
||||
'speakers': [
|
||||
speaker.serialize()
|
||||
'speaker_slugs': [
|
||||
speaker.slug
|
||||
for speaker in self.speakers.all()
|
||||
],
|
||||
'video_recording': self.video_recording,
|
||||
|
@ -613,10 +615,27 @@ class Speaker(CampRelatedModel):
|
|||
def get_absolute_url(self):
|
||||
return reverse_lazy('speaker_detail', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug})
|
||||
|
||||
def get_picture_url(self, size):
|
||||
return reverse('speaker_picture', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug, 'picture': size})
|
||||
|
||||
def get_small_picture_url(self):
|
||||
return self.get_picture_url('thumbnail')
|
||||
|
||||
def get_large_picture_url(self):
|
||||
return self.get_picture_url('large')
|
||||
|
||||
|
||||
def serialize(self):
|
||||
data = {
|
||||
'name': self.name,
|
||||
'slug': self.slug,
|
||||
'biography': self.biography,
|
||||
}
|
||||
|
||||
if self.picture_small and self.picture_large:
|
||||
data['large_picture_url'] = self.get_large_picture_url()
|
||||
data['small_picture_url'] = self.get_small_picture_url()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue