Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Create Update Feed #117

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
29a28ca
adding migrations and models from events and timeline_events, these a…
aajjbb Jul 28, 2017
5e49de5
adapting following/flows to generate a event when a following occurs
aajjbb Jul 28, 2017
4c90eb3
adapting tests from models to check event creation and testing the un…
aajjbb Jul 28, 2017
7b195b2
fixing typos
aajjbb Jul 28, 2017
535cc1e
adding a primary key to timeline_events
aajjbb Aug 5, 2017
094f399
adding timeline_event to models and tests
aajjbb Aug 5, 2017
a9e44c1
starting to sketch an ui for timeline, currently it only lists small …
aajjbb Aug 5, 2017
212aece
fixing a typo
aajjbb Aug 5, 2017
2c8b3f2
writing a simple logic to present follow/bookmark events
aajjbb Aug 5, 2017
267b290
creating deliver method for TimelineEvents, adding an update event to…
aajjbb Aug 5, 2017
29738fc
renamed the source of events and remove the type field as the source …
aajjbb Aug 5, 2017
2391d72
add an event to the followed/starred user when it's profile is follow…
aajjbb Aug 5, 2017
2a4e099
adapting the events model to use source_user_id
aajjbb Aug 7, 2017
e20ee34
adapting model, flow and tests to consider events to have source_user_id
aajjbb Aug 7, 2017
6040002
merging with upstream/master
aajjbb Aug 8, 2017
962eeda
readding events and timeline_events tables in migrations
aajjbb Aug 9, 2017
ab657c7
adapt classes to use \create method with opts arguments
aajjbb Aug 9, 2017
a9206c5
adding relations of timeline_events and starting to use preload to av…
aajjbb Aug 11, 2017
19c4ef4
using preload correctly with lapis updated version
aajjbb Aug 13, 2017
8517657
removing the useless object_ prefix of the object columns in events, …
aajjbb Aug 13, 2017
d5707fa
fix import scope and weird formatting in tests
aajjbb Aug 13, 2017
11e7091
creates a flow to manage events, it manage event creation and it's de…
aajjbb Aug 17, 2017
1840b30
create a dedicated page for the timeline
aajjbb Aug 17, 2017
fac47c3
fixing timeline_event removal bug
aajjbb Aug 18, 2017
cecbecf
adding time_ago_in_words in timeline_events widget
aajjbb Aug 18, 2017
20f9167
remove useless lines from module tests
aajjbb Aug 21, 2017
6bd7539
add event creation policy to user following
aajjbb Aug 21, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app.moon
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,6 @@ class MoonRocks extends lapis.Application
@top_depended = [t for t in *@top_depended when t.manifest_module]

render: true

[timeline: "/timeline"]: require_login capture_errors_404 =>
render: true
4 changes: 4 additions & 0 deletions applications/modules.moon
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ class MoonRocksModules extends lapis.Application
"invalid module"

@flow("followings")\follow_object @module, @params.type
@flow("events")\create_event_and_deliver @module, @params.type

redirect_to: @url_for @module

[unfollow_module: "/module/:module_id/unfollow/:type"]: require_login capture_errors_404 =>
Expand All @@ -222,4 +224,6 @@ class MoonRocksModules extends lapis.Application
"invalid module"

@flow("followings")\unfollow_object @module, @params.type
@flow("events")\remove_from_timeline @module, @params.type

redirect_to: @url_for @module
2 changes: 2 additions & 0 deletions applications/user.moon
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ class MoonRocksUser extends lapis.Application
"You can't follow yourself"

@flow("followings")\follow_object followed_user, "subscription"
@flow("events")\create_event_and_deliver followed_user, "subscription"

redirect_to: @url_for followed_user

Expand All @@ -372,5 +373,6 @@ class MoonRocksUser extends lapis.Application
"Invalid User"

@flow("followings")\unfollow_object unfollowed_user, "subscription"
@flow("events")\remove_from_timeline unfollowed_user, "subscription"

redirect_to: @url_for unfollowed_user
60 changes: 60 additions & 0 deletions flows/events.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Flow from require "lapis.flow"

import Events, Followings, TimelineEvents, Users from require "models"

import assert_error from require "lapis.application"

class EventsFlow extends Flow
expose_assigns: true

new: (...) =>
super ...
assert_error @current_user, "must be logged in"

create_event_and_deliver: (object, event_type) =>
import preload from require "lapis.db.model"

-- Creates the primary event
event = Events\create({
user: @current_user
object: object
event_type: Events.event_types\for_db event_type
})

-- Adds the new event to the timeline of every subscriber of @current_user
do
user_followers = Followings\select "where object_type = ? and object_id = ? and type = ?", Followings\object_type_for_object(@current_user), @current_user.id, Followings.types.subscription
preload user_followers, "source_user"

for user in *user_followers
follower_user = user.source_user

TimelineEvents\create({
user_id: follower_user.id
event_id: event.id
})

-- If the event is a update, then every follower of the module should see the event
if Events.event_types.update == Events.event_types\for_db(event_type)
followers = Followings\select "where object_id = ? and object_type = ? and type = ?", event.object_id, event.object_type, Followings.types.subscription

preload followers, "source_user"

for users in *followers
follower_user = users.source_user
TimelineEvents\create({
user_id: follower_user.id
event_id: event.id
})

remove_from_timeline: (object, event_type) =>
db = require "lapis.db"
timeline_events = if Events\object_type_for_object(object) == Events\object_type_for_model(Users)
-- If we are removing the subscription from an use
db.select "user_id, event_id from timeline_events join events on timeline_events.event_id = events.id and events.source_user_id = ?", object.id
else
-- If we are removing the subscription from Module
db.select "user_id, event_id from timeline_events join events on timeline_events.event_id = events.id and events.source_user_id = ? and events.object_type = ? and object_id = ?", object.user_id, Events\object_type_for_object(object), object.id

for timeline_entry in *timeline_events
TimelineEvents\delete(timeline_entry.user_id, timeline_entry.event_id)
3 changes: 2 additions & 1 deletion flows/followings.moon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import Flow from require "lapis.flow"

import Followings, Notifications from require "models"
import Events, Followings, Notifications, TimelineEvents, Users from require "models"

import assert_error from require "lapis.application"

Expand All @@ -13,6 +13,7 @@ class FollowingsFlow extends Flow
assert_error @current_user, "must be logged in"

follow_object: (object, type) =>

f = Followings\create {
source_user_id: @current_user.id
:object
Expand Down
19 changes: 19 additions & 0 deletions migrations.moon
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,23 @@ import
[1500318771]: =>
db.query "alter table followings drop constraint followings_pkey"
db.query "alter table followings add primary key(source_user_id, object_type, object_id, type)"

[1501182931]: =>
create_table "events", {
{"id", serial}
{"event_type", enum}
{"source_user_id", foreign_key}
{"object_id", foreign_key}
{"object_type", enum}
{"created_at", time}
{"updated_at", time}

"PRIMARY KEY (id)"
}
create_table "timeline_events", {
{"user_id", foreign_key}
{"event_id", foreign_key}

"PRIMARY KEY (user_id, event_id)"
}
}
43 changes: 43 additions & 0 deletions models/events.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
db = require "lapis.db"

import Model, enum from require "lapis.db.model"
import safe_insert from require "helpers.models"

class Events extends Model
@timestamp: true

@event_types: enum {
subscription: 1
bookmark: 2
update: 3
}

@relations: {
{"source_user", belongs_to: "Users"}
{"object", polymorphic_belongs_to: {
[1]: {"module", "Modules"}
[2]: {"user", "Users"}
}}
}

@create: (opts={}) =>
assert opts.user, "missing event's user"
assert opts.object, "missing event's object"
assert opts.event_type, "missing event_type, events must have a type"

event_opts = {
event_type: opts.event_type
source_user_id: opts.user.id
object_id: opts.object.id
object_type: @@object_type_for_object opts.object
}

event = safe_insert @, event_opts

return event

delete: () =>
import TimelineEvents from require "models"

db.delete @@table_name!, { id: @id }
db.delete TimelineEvents\table_name!, { event_id: @id}
30 changes: 30 additions & 0 deletions models/timeline_events.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
db = require "lapis.db"
import Model from require "lapis.db.model"


class TimelineEvents extends Model
@primary_key: { "user_id", "event_id" }

@relations: {
{"user", belongs_to: "Users"}
{"event", belongs_to: "Events"}
}

@create: (opts={}) =>
assert opts.user_id, "user id not specified"
assert opts.event_id, "event id not specified"

super {
user_id: opts.user_id
event_id: opts.event_id
}

@delete: (user_id, event_id) =>
db.delete @table_name!, { user_id: user_id, event_id: event_id }

@user_timeline: (user) =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably want to either paginate this, or limit to something reasonable like 50 for the time being

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm limiting by 50 but I plan to add pagination soon.

import preload from require "lapis.db.model"
timeline = @@select "where user_id = ? limit 50", user.id
preload timeline, "user"
preload timeline, event: "object"
return timeline
4 changes: 4 additions & 0 deletions models/users.moon
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,7 @@ class Users extends Model

uuid = generate_uuid()
"#{username}-#{uuid\gsub("-", "")\sub 1, 10}"

timeline: () =>
import TimelineEvents from require "models"
TimelineEvents\user_timeline @
12 changes: 9 additions & 3 deletions spec/applications/modules_spec.moon
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import request_as from require "spec.helpers"
factory = require "spec.factory"


import Modules, Versions, Followings, Users, Notifications, NotificationObjects from require "models"


describe "applications.modules", ->
import Modules, Versions, Events, Followings, Users, Notifications, NotificationObjects from require "spec.models"
use_test_server!

before_each ->
truncate_tables Modules, Versions, Followings, Users, Notifications, NotificationObjects

it "follows module", ->
current_user = factory.Users!
Expand All @@ -21,8 +21,12 @@ describe "applications.modules", ->
assert.same 302, status

followings = Followings\select!
events = Events\select!
user_timeline = current_user\timeline!

assert.same 1, #followings
assert.same 1, #events

following = unpack followings

assert.same current_user.id, following.source_user_id
Expand All @@ -44,7 +48,10 @@ describe "applications.modules", ->
assert.same 302, status

followings = Followings\select!
events = Events\select!

assert.same 0, #followings
assert.same 0, #events
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails because of the comment mentioned above in flows/followings.moon

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently "only" in tests the event created in follow_object can't be found and isn't deleted.


current_user\refresh!
mod\refresh!
Expand Down Expand Up @@ -114,4 +121,3 @@ describe "applications.modules", ->

assert.same 0, Notifications\count!
assert.same 0, NotificationObjects\count!

79 changes: 79 additions & 0 deletions spec/models/events_spec.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import use_test_env from require "lapis.spec"

factory = require "spec.factory"

describe "models.events", ->
import Events, Modules, Users, TimelineEvents from require "spec.models"

use_test_env!

it "creates an event of user following user", ->
user = factory.Users!
followed_user = factory.Users!

event = Events\create({
user: user
object: followed_user
event_type: Events.event_types.subscription
})

user_timeline = user\timeline!

assert.same user.id, event.source_user_id
assert.same followed_user.id, event.object_id
assert.same event.event_type, Events.event_types.subscription

assert.same, #user_timeline, 1

it "creates an event of user following a module", ->
user = factory.Users!
module = factory.Modules!

event = Events\create({
user: user
object: module
event_type: Events.event_types.subscription
})

user_timeline = user\timeline!

assert.same user.id, event.source_user_id
assert.same module.id, event.object_id
assert.same event.event_type, Events.event_types.subscription

assert.same, #user_timeline, 1

it "creates an event of user starring a module", ->
user = factory.Users!
module = factory.Modules!

event = Events\create({
user: user
object: module
event_type: Events.event_types.bookmark
})

user_timeline = user\timeline!

assert.same user.id, event.source_user_id
assert.same module.id, event.object_id
assert.same event.event_type, Events.event_types.bookmark

assert.same, #user_timeline, 1

it "deletes an event", ->
user = factory.Users!
module = factory.Modules!

event = Events\create({
user: user
object: module
event_type: Events.event_types.bookmark
})

event_id = event.id

event\delete!

assert.same Events\find(event_id), nil
assert.same 0, #user\timeline!
2 changes: 0 additions & 2 deletions views/index.moon
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class Index extends require "widgets.page"
@inner_content!

inner_content: =>

div class: "home_columns", ->
div class: "column", ->
h2 ->
Expand Down Expand Up @@ -63,4 +62,3 @@ class Index extends require "widgets.page"

script type: "text/javascript", ->
raw "new M.Index(#{@widget_selector!}, #{to_json @downloads_daily});"

8 changes: 8 additions & 0 deletions views/timeline.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
TimelineEvents = require "widgets.timeline_events"

class Timeline extends require "widgets.page"
inner_content: =>
h2 ->
text "Timeline"
widget TimelineEvents!

Loading