| rev | line source | 
| bsw/jbe@0 | 1 Issue = mondelefant.new_class() | 
| bsw/jbe@0 | 2 Issue.table = 'issue' | 
| bsw/jbe@0 | 3 | 
| bsw@549 | 4 local new_selector = Issue.new_selector | 
| bsw@549 | 5 | 
| bsw@549 | 6 function Issue:new_selector() | 
| bsw@549 | 7   local selector = new_selector(self) | 
| bsw@1234 | 8   selector:add_field("justify_interval(min_admission_time)::text", "min_admission_time_text") | 
| bsw@1234 | 9   selector:add_field("justify_interval(max_admission_time)::text", "max_admission_time_text") | 
| bsw@935 | 10   selector:add_field("justify_interval(discussion_time)::text", "discussion_time_text") | 
| bsw@935 | 11   selector:add_field("justify_interval(verification_time)::text", "verification_time_text") | 
| bsw@935 | 12   selector:add_field("justify_interval(voting_time)::text", "voting_time_text") | 
| bsw@1234 | 13   selector:add_field("justify_interval(coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.max_admission_time) - now())", "state_time_left") | 
| bsw@935 | 14   selector:add_field("justify_interval(now() - issue.closed)", "closed_ago") | 
| bsw@549 | 15   return selector | 
| bsw@549 | 16 end | 
| bsw@549 | 17 | 
| bsw/jbe@0 | 18 Issue:add_reference{ | 
| bsw/jbe@0 | 19   mode          = 'm1', | 
| bsw/jbe@0 | 20   to            = "Area", | 
| bsw/jbe@0 | 21   this_key      = 'area_id', | 
| bsw/jbe@0 | 22   that_key      = 'id', | 
| bsw/jbe@0 | 23   ref           = 'area', | 
| bsw/jbe@0 | 24 } | 
| bsw/jbe@0 | 25 | 
| bsw/jbe@0 | 26 Issue:add_reference{ | 
| bsw/jbe@0 | 27   mode          = 'm1', | 
| bsw/jbe@0 | 28   to            = "Policy", | 
| bsw/jbe@0 | 29   this_key      = 'policy_id', | 
| bsw/jbe@0 | 30   that_key      = 'id', | 
| bsw/jbe@0 | 31   ref           = 'policy', | 
| bsw/jbe@0 | 32 } | 
| bsw/jbe@0 | 33 | 
| bsw/jbe@0 | 34 Issue:add_reference{ | 
| bsw/jbe@0 | 35   mode          = '1m', | 
| bsw/jbe@0 | 36   to            = "Initiative", | 
| bsw/jbe@0 | 37   this_key      = 'id', | 
| bsw/jbe@0 | 38   that_key      = 'issue_id', | 
| bsw/jbe@0 | 39   ref           = 'initiatives', | 
| bsw@551 | 40   back_ref      = 'issue', | 
| bsw@982 | 41   default_order = 'initiative.admitted DESC NULLS LAST, initiative.rank NULLS LAST, initiative.harmonic_weight DESC NULLS LAST, id' | 
| bsw/jbe@0 | 42 } | 
| bsw/jbe@0 | 43 | 
| bsw/jbe@0 | 44 Issue:add_reference{ | 
| bsw/jbe@0 | 45   mode          = '1m', | 
| bsw/jbe@0 | 46   to            = "Interest", | 
| bsw/jbe@0 | 47   this_key      = 'id', | 
| bsw/jbe@0 | 48   that_key      = 'issue_id', | 
| bsw/jbe@0 | 49   ref           = 'interests', | 
| bsw/jbe@0 | 50   back_ref      = 'issue', | 
| bsw/jbe@0 | 51   default_order = '"id"' | 
| bsw/jbe@0 | 52 } | 
| bsw/jbe@0 | 53 | 
| bsw/jbe@0 | 54 Issue:add_reference{ | 
| bsw/jbe@0 | 55   mode          = '1m', | 
| bsw/jbe@0 | 56   to            = "Supporter", | 
| bsw/jbe@0 | 57   this_key      = 'id', | 
| bsw/jbe@0 | 58   that_key      = 'issue_id', | 
| bsw/jbe@0 | 59   ref           = 'supporters', | 
| bsw/jbe@0 | 60   back_ref      = 'issue', | 
| bsw/jbe@0 | 61   default_order = '"id"' | 
| bsw/jbe@0 | 62 } | 
| bsw/jbe@0 | 63 | 
| bsw/jbe@0 | 64 Issue:add_reference{ | 
| bsw/jbe@0 | 65   mode          = '1m', | 
| bsw/jbe@0 | 66   to            = "DirectVoter", | 
| bsw/jbe@0 | 67   this_key      = 'id', | 
| bsw/jbe@0 | 68   that_key      = 'issue_id', | 
| bsw/jbe@0 | 69   ref           = 'direct_voters', | 
| bsw/jbe@0 | 70   back_ref      = 'issue', | 
| bsw/jbe@0 | 71   default_order = '"member_id"' | 
| bsw/jbe@0 | 72 } | 
| bsw/jbe@0 | 73 | 
| bsw/jbe@0 | 74 Issue:add_reference{ | 
| bsw/jbe@0 | 75   mode          = '1m', | 
| bsw/jbe@0 | 76   to            = "Vote", | 
| bsw/jbe@0 | 77   this_key      = 'id', | 
| bsw/jbe@0 | 78   that_key      = 'issue_id', | 
| bsw/jbe@0 | 79   ref           = 'votes', | 
| bsw/jbe@0 | 80   back_ref      = 'issue', | 
| bsw/jbe@0 | 81   default_order = '"member_id", "initiative_id"' | 
| bsw/jbe@0 | 82 } | 
| bsw/jbe@0 | 83 | 
| bsw/jbe@0 | 84 Issue:add_reference{ | 
| bsw@2 | 85   mode          = '1m', | 
| bsw@2 | 86   to            = "Delegation", | 
| bsw@2 | 87   this_key      = 'id', | 
| bsw@2 | 88   that_key      = 'issue_id', | 
| bsw@2 | 89   ref           = 'delegations', | 
| bsw@2 | 90   back_ref      = 'issue' | 
| bsw@2 | 91 } | 
| bsw@2 | 92 | 
| bsw@2 | 93 Issue:add_reference{ | 
| bsw/jbe@0 | 94   mode                  = 'mm', | 
| bsw/jbe@0 | 95   to                    = "Member", | 
| bsw/jbe@0 | 96   this_key              = 'id', | 
| bsw/jbe@0 | 97   that_key              = 'id', | 
| bsw/jbe@0 | 98   connected_by_table    = 'interest', | 
| bsw/jbe@0 | 99   connected_by_this_key = 'issue_id', | 
| bsw/jbe@0 | 100   connected_by_that_key = 'member_id', | 
| bsw/jbe@0 | 101   ref                   = 'members' | 
| bsw/jbe@0 | 102 } | 
| bsw/jbe@0 | 103 | 
| bsw@3 | 104 Issue:add_reference{ | 
| bsw@3 | 105   mode                  = 'mm', | 
| bsw@3 | 106   to                    = "Member", | 
| bsw@3 | 107   this_key              = 'id', | 
| bsw@3 | 108   that_key              = 'id', | 
| bsw@3 | 109   connected_by_table    = 'direct_interest_snapshot', | 
| bsw@3 | 110   connected_by_this_key = 'issue_id', | 
| bsw@3 | 111   connected_by_that_key = 'member_id', | 
| bsw@3 | 112   ref                   = 'interested_members_snapshot' | 
| bsw@3 | 113 } | 
| bsw@3 | 114 | 
| bsw/jbe@6 | 115 Issue:add_reference{ | 
| bsw/jbe@6 | 116   mode                  = 'mm', | 
| bsw/jbe@6 | 117   to                    = "Member", | 
| bsw/jbe@6 | 118   this_key              = 'id', | 
| bsw/jbe@6 | 119   that_key              = 'id', | 
| bsw/jbe@6 | 120   connected_by_table    = 'direct_voter', | 
| bsw/jbe@6 | 121   connected_by_this_key = 'issue_id', | 
| bsw/jbe@6 | 122   connected_by_that_key = 'member_id', | 
| bsw/jbe@6 | 123   ref                   = 'direct_voters' | 
| bsw/jbe@6 | 124 } | 
| bsw/jbe@6 | 125 | 
| bsw@525 | 126 Issue:add_reference{ | 
| bsw@525 | 127   mode               = "11", | 
| bsw@525 | 128   to                 = mondelefant.class_prototype, | 
| bsw@525 | 129   this_key           = "id", | 
| bsw@525 | 130   that_key           = "issue_id", | 
| bsw@547 | 131   ref                = "member_info", | 
| bsw@525 | 132   back_ref           = "issue", | 
| bsw@525 | 133   selector_generator = function(list, options) | 
| bsw@547 | 134     assert(options.member_id, "member_id mandatory for member_info") | 
| bsw@525 | 135     local ids = { sep = ", " } | 
| bsw@525 | 136     for i, object in ipairs(list) do | 
| bsw@525 | 137       local id = object.id | 
| bsw@525 | 138       if id ~= nil then | 
| bsw@525 | 139         ids[#ids+1] = {"?", id} | 
| bsw@525 | 140       end | 
| bsw@525 | 141     end | 
| bsw@525 | 142     local sub_selector = Issue:get_db_conn():new_selector() | 
| bsw@525 | 143     if #ids == 0 then | 
| bsw@525 | 144       return sub_selector:empty_list_mode() | 
| bsw@525 | 145     end | 
| bsw@1045 | 146     sub_selector:from ( "issue" ) | 
| bsw@1045 | 147     sub_selector:add_field ( "issue.id", "issue_id" ) | 
| bsw@1045 | 148     sub_selector:add_field { '(delegation_info(?, null, null, issue.id, ?)).*', options.member_id, options.trustee_id } | 
| bsw@1045 | 149     sub_selector:add_where { 'issue.id IN ($)', ids } | 
| bsw@525 | 150 | 
| bsw@525 | 151     local selector = Issue:get_db_conn():new_selector() | 
| bsw@1045 | 152     selector:add_from ( "issue" ) | 
| bsw@1045 | 153     selector:join(sub_selector, "delegation_info", "delegation_info.issue_id = issue.id" ) | 
| bsw@1045 | 154     selector:left_join ( "member", "first_trustee", "first_trustee.id = delegation_info.first_trustee_id" ) | 
| bsw@1045 | 155     selector:left_join ( "member", "other_trustee", "other_trustee.id = delegation_info.other_trustee_id" ) | 
| bsw@1045 | 156     selector:add_field ( "delegation_info.*" ) | 
| bsw@1045 | 157     selector:add_field ( "first_trustee.name", "first_trustee_name" ) | 
| bsw@1045 | 158     selector:add_field ( "other_trustee.name", "other_trustee_name" ) | 
| bsw@1045 | 159     selector:left_join ( "direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", options.member_id }) | 
| bsw@1045 | 160     selector:add_field ( "direct_voter.member_id NOTNULL", "direct_voted") | 
| bsw@1045 | 161     selector:left_join ( "non_voter", nil, { "non_voter.issue_id = issue.id AND non_voter.member_id = ?", options.member_id }) | 
| bsw@1045 | 162     selector:add_field ( "non_voter.member_id NOTNULL", "non_voter" ) | 
| bsw@1045 | 163     selector:left_join ( "direct_interest_snapshot", nil, { [[ | 
| bsw@1045 | 164       direct_interest_snapshot.issue_id = issue.id AND | 
| bsw@1045 | 165       direct_interest_snapshot.event = issue.latest_snapshot_event AND | 
| bsw@1045 | 166       direct_interest_snapshot.member_id = ? | 
| bsw@1045 | 167     ]], options.member_id }) | 
| bsw@1045 | 168     selector:add_field ( "direct_interest_snapshot.weight", "weight" ) | 
| bsw@525 | 169     return selector | 
| bsw@525 | 170   end | 
| bsw@525 | 171 } | 
| bsw@525 | 172 | 
| bsw@547 | 173 function Issue.list:load_everything_for_member_id(member_id) | 
| bsw@547 | 174   local areas = self:load("area") | 
| bsw@547 | 175   areas:load("unit") | 
| bsw@547 | 176   self:load("policy") | 
| bsw@551 | 177   if member_id then | 
| bsw@551 | 178     self:load("member_info", { member_id = member_id }) | 
| bsw@551 | 179   end | 
| bsw@551 | 180   local initiatives = self:load("initiatives") | 
| bsw@551 | 181   initiatives:load_everything_for_member_id(member_id) | 
| bsw@525 | 182 end | 
| bsw@525 | 183 | 
| bsw@548 | 184 function Issue.object:load_everything_for_member_id(member_id) | 
| bsw@548 | 185   local areas = self:load("area") | 
| bsw@548 | 186   areas:load("unit") | 
| bsw@548 | 187   self:load("policy") | 
| bsw@551 | 188   if member_id then | 
| bsw@551 | 189     self:load("member_info", { member_id = member_id }) | 
| bsw@551 | 190   end | 
| bsw@551 | 191   local initiatives = self:load("initiatives") | 
| bsw@551 | 192   initiatives:load_everything_for_member_id(member_id) | 
| bsw@548 | 193 end | 
| bsw@548 | 194 | 
| bsw@972 | 195 | 
| bsw@972 | 196 | 
| bsw/jbe@0 | 197 function Issue:get_state_name_for_state(value) | 
| bsw@2 | 198   local state_name_table = { | 
| bsw@1045 | 199     admission = _"Admission", | 
| bsw@972 | 200     discussion = _"Discussion", | 
| bsw@1045 | 201     verification = _"Verification", | 
| bsw@972 | 202     voting = _"Voting", | 
| bsw@1045 | 203     canceled_revoked_before_accepted = _"Revoked (during admission)", | 
| bsw@1045 | 204     canceled_issue_not_accepted = _"Failed 1st quorum", | 
| bsw@1045 | 205     canceled_after_revocation_during_discussion = _"Revoked (during discussion)", | 
| bsw@1045 | 206     canceled_after_revocation_during_verification = _"Revoked (during verification)", | 
| bsw@1029 | 207     canceled_by_admin = _"Canceled by administrative intervention", | 
| bsw@972 | 208     calculation = _"Calculation", | 
| bsw@1045 | 209     canceled_no_initiative_admitted = _"All initiatives failed 2nd quorum", | 
| bsw@1045 | 210     finished_without_winner = _"Disapproved", | 
| bsw@1045 | 211     finished_with_winner = _"Finished with winner", | 
| bsw@2 | 212   } | 
| bsw@10 | 213   return state_name_table[value] or value or '' | 
| bsw/jbe@0 | 214 end | 
| bsw/jbe@0 | 215 | 
| bsw@972 | 216 | 
| bsw@972 | 217 | 
| bsw@2 | 218 function Issue:get_search_selector(search_string) | 
| bsw/jbe@0 | 219   return self:new_selector() | 
| bsw/jbe@0 | 220     :join('"initiative"', nil, '"initiative"."issue_id" = "issue"."id"') | 
| bsw/jbe@6 | 221     :join('"draft"', nil, '"draft"."initiative_id" = "initiative"."id"') | 
| bsw/jbe@6 | 222     :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?) OR "draft"."text_search_data" @@ "text_search_query"(?)', search_string, search_string } | 
| bsw/jbe@6 | 223     :add_group_by('"issue"."id"') | 
| bsw/jbe@0 | 224 end | 
| bsw/jbe@0 | 225 | 
| bsw@75 | 226 function Issue:modify_selector_for_state(initiatives_selector, state) | 
| bsw@51 | 227   if state == "new" then | 
| bsw@75 | 228     initiatives_selector:add_where("issue.accepted ISNULL AND issue.closed ISNULL") | 
| bsw@51 | 229   elseif state == "accepted" then | 
| bsw@75 | 230     initiatives_selector:add_where("issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL") | 
| bsw@51 | 231   elseif state == "frozen" then | 
| bsw@75 | 232     initiatives_selector:add_where("issue.half_frozen NOTNULL AND issue.fully_frozen ISNULL AND issue.closed ISNULL") | 
| bsw@51 | 233   elseif state == "voting" then | 
| bsw@75 | 234     initiatives_selector:add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL") | 
| bsw@51 | 235   elseif state == "finished" then | 
| bsw@75 | 236     initiatives_selector:add_where("issue.fully_frozen NOTNULL AND issue.closed NOTNULL") | 
| bsw@973 | 237   elseif state == "canceled" then | 
| bsw@75 | 238     initiatives_selector:add_where("issue.fully_frozen ISNULL AND issue.closed NOTNULL") | 
| bsw@75 | 239   else | 
| bsw@75 | 240     error("Invalid state") | 
| bsw@51 | 241   end | 
| bsw@51 | 242 end | 
| bsw@51 | 243 | 
| bsw@2 | 244 | 
| bsw@2 | 245 function Issue.object_get:state_name() | 
| bsw@2 | 246   return Issue:get_state_name_for_state(self.state) | 
| bsw@2 | 247 end | 
| bsw@2 | 248 | 
| bsw@2 | 249 function Issue.object_get:next_states_names() | 
| bsw@2 | 250   local next_states = self.next_states | 
| bsw@2 | 251   if not next_states then | 
| bsw@2 | 252     return | 
| bsw@2 | 253   end | 
| bsw@2 | 254   local state_names = {} | 
| bsw@2 | 255   for i, state in ipairs(self.next_states) do | 
| bsw@2 | 256     state_names[#state_names+1] = Issue:get_state_name_for_state(state) | 
| bsw@2 | 257   end | 
| bsw@2 | 258   return table.concat(state_names, ", ") | 
| bsw@525 | 259 end | 
| bsw@525 | 260 | 
| bsw@525 | 261 function Issue.object_get:etherpad_url() | 
| bsw@525 | 262   return config.etherpad.base_url .. "p/" .. config.etherpad.group_id .. "$Issue" .. self.id | 
| bsw@551 | 263 end | 
| bsw@1045 | 264 | 
| bsw@1045 | 265 function Issue.object_get:name() | 
| bsw@1045 | 266   return self.policy.name .. " #" .. self.id | 
| bsw@1045 | 267 end | 
| bsw@1045 | 268 | 
| bsw@1045 | 269 function Issue.object_get:state_time_text() | 
| bsw@1045 | 270   if self.closed then | 
| bsw@1045 | 271     return _("#{closed_ago} ago", { closed_ago = self.closed_ago }) | 
| bsw@1045 | 272   elseif string.sub(self.state_time_left, 1, 2) ~= "-" then | 
| bsw@1045 | 273     return _("ends soon", { state_time_left = self.state_time_left }) | 
| bsw@1045 | 274   else | 
| bsw@1045 | 275     return _("ends in #{state_time_left}", { state_time_left = self.state_time_left }) | 
| bsw@1045 | 276   end | 
| bsw@1045 | 277 end |