Commit 725d0f40 authored by hugo's avatar hugo
Browse files

Rollout version

parent 772d0628
......@@ -331,12 +331,21 @@ get '/api/statistics/branch/:period_type/:branch_id' do
results << AccumulatedBranch.includes(report_method.to_sym).first.send(report_method).with_aggregate.all.ordered_by_date
else
branch_list.each do |branch_id|
results << statistics_class.with_aggregate.where(aggregate_id: branch_list, aggregate_type: 'Branch').order(:date)
# TODO: the offset is an emergency solution as the page rendering started taking too long
# Come up with something more clever
offset = statistics_class.with_aggregate.where(aggregate_id: branch_list, aggregate_type: 'Branch').length
offset = offset < 365 ? 0 : offset - 365
results << statistics_class.with_aggregate.where(aggregate_id: branch_list, aggregate_type: 'Branch').order(:date).offset(offset)
branch = Branch.find(branch_id)
status_list << branch.get_recent_camera_status
end
end
data = results.flatten
data_size = data.size < 365 ? data.size : 365
data = data.fetch(-data_size)
{last_update: SystemStatus.get_last_updated_as_string,
data: results.flatten, camera_status: status_list.flatten}.to_json
end
......@@ -394,14 +403,18 @@ get '/adm/manage_assets' do
Branch.find(params[:branch_id]) : Branch.first rescue halt(400, 'Ugyldig filial-ID')
entrances = selected_branch.entrances
if params[:entrance_id] == 'all' || params[:entrance_id].to_i == 0
selected_entrance = nil
selected_entrance = params[:entrance_id].to_i > 0 ?
entrances.find(params[:entrance_id]) : selected_branch.entrances.first rescue halt(400, 'Ugyldig inngangs-ID')
edit_entrance_mode = params[:edit_entrance_mode].to_s.downcase == "true"
if edit_entrance_mode
in_edit_entrance = params[:entrance_id].blank? ? nil : selected_entrance
else
selected_entrance = Entrance.find(params[:entrance_id]) rescue halt(400, 'Ugyldig inngangs-ID')
in_edit_entrance = selected_entrance
end
# determine counters and interfaces
counters = selected_entrance&.counters || selected_branch.counters
counters = selected_entrance.counters
selected_counter = params[:counter_id].to_i > 0 ?
counters.find(params[:counter_id]) : nil rescue nil
......@@ -424,9 +437,10 @@ get '/adm/manage_assets' do
erb ('manage_assets_' + selected_asset).to_sym, locals: {
branches: Branch.all,
selected_branch: selected_branch,
edit_entrances_mode: params[:edit_entrance_mode].to_s.downcase == "true",
edit_entrance_mode: edit_entrance_mode,
entrances: entrances,
selected_entrance: selected_entrance,
in_edit_entrance: in_edit_entrance,
primary_source_periods: selected_entrance&.primary_source_periods,
selected_psp: (PrimarySourcePeriod.find(params[:psp_id]) rescue nil), # TODO: fix
interfaces: interfaces,
......
......@@ -56,12 +56,9 @@ class DataCollector
def collect_stats_for (branch, counter, date, has_oh)
response = nil
status = false
counter.collect_counts(date)
counter.errors.full_messages.each do |error|
puts error
@logger.log("warn", error)
end
end
......
......@@ -86,7 +86,7 @@ UPDATE counters set counter_interface_id = 1 where source_type_id = 1;
UPDATE counters set counter_interface_id = 4 where source_type_id = 2;
UPDATE counters set counter_interface_id = 2 where id = 6;
# NOTE portal 9 is still unassigned
# ALTER TABLE cameras DROP branch_id;
# ALTER TABLE portals DROP branch_id;
......@@ -159,7 +159,7 @@ UPDATE counters set entrance_id = 17 where id = 15;
UPDATE counters set entrance_id = 18 where id = 16;
UPDATE counters set entrance_id = 14 where id = 17;
UPDATE counters set entrance_id = 20 where id = 18;
UPDATE counters set entrance_id = 19 where id = 19;
UPDATE counters set entrance_id = 21 where id = 19;
UPDATE counters set entrance_id = 22 where id = 20;
UPDATE counters set entrance_id = 15 where id = 21;
UPDATE counters set entrance_id = 23 where id = 22;
......@@ -169,7 +169,7 @@ UPDATE counters set entrance_id = 26 where id = 25;
UPDATE counters set entrance_id = 16 where id = 26;
UPDATE counters set entrance_id = 18 where id = 27;
UPDATE counters set entrance_id = 19 where id = 28;
UPDATE counters set entrance_id = 0 where id = 29;
UPDATE counters set entrance_id = 22 where id = 29;
UPDATE counters set entrance_id = 11 where id = 30;
UPDATE counters set entrance_id = 12 where id = 31;
UPDATE counters set entrance_id = 13 where id = 32;
......
......@@ -12,7 +12,7 @@ class Counter < ActiveRecord::Base
belongs_to :counter_interface
belongs_to :entrance
has_many :primary_source_periods # TODO: should this be here?
has_many :primary_source_periods
has_many :daily_statistics, as: :aggregate
has_many :weekly_statistics, as: :aggregate
......@@ -30,6 +30,11 @@ class Counter < ActiveRecord::Base
#---------------
def collect_counts(date)
#if !counter_interface.respond_to?(download_counts)
# errors.add[:program_code, "Ineligible action: collect_counts for #{counter.name}"]
# return
#end
csv = counter_interface.download_counts(self, date)
errors.merge!(counter_interface.errors)
......@@ -46,7 +51,7 @@ class Counter < ActiveRecord::Base
valid_slots = [0, 15, 30, 45]
if timestamp.sec == 0 && valid_slots.include?(timestamp.min)
# TODO interrim fix for new camera having different csv headers
# TODO: interrim fix for new camera having different csv headers
visitors_in = row[:pedestrians_coming_in] || row[:in]
visitors_out = row[:pedestrians_going_out] || row[:out]
......@@ -78,6 +83,10 @@ class Counter < ActiveRecord::Base
CounterInterface.where(source_type: source_type)
end
def active?
is_active
end
def check_integrity
# TODO: check integrity for source_periods, count_corrections, etc.
end
......@@ -166,9 +175,9 @@ class Counter < ActiveRecord::Base
end
end
# ----- methods for handling active dates ----
# ----- methods for handling active dates ---- 27-02-2019
def dates_within_range?(first_date, last_date)
first_active_date && last_active_date && first_active_date <= last_date && last_active_date >= first_date
first_active_date && first_active_date <= last_date && last_active_date_or_today >= first_date
end
# likely won't need these
......@@ -285,6 +294,7 @@ class Portal < ActiveRecord::Base
require 'csv'
has_one :counter, :as => :source, dependent: :destroy
has_one :branch, through: :counter
has_many :downtimes, :through => :counter
accepts_nested_attributes_for :counter
......
......@@ -20,9 +20,9 @@ class PeopleCounter < CounterInterface
status = false
begin
response = http.request(request)
response = http.request(request)
response_body = response.body
status = (response.code == '200')
status = (response.code == '200')
rescue => e
errors.add(:request, "Request failed for #{counter.name} with #{}: #{e.message}")
end
......@@ -64,9 +64,9 @@ class PeopleCounterStereo < CounterInterface
status = false
begin
response = http.request(request)
response = http.request(request)
response_body = response.body
status = (response.code == '200')
status = (response.code == '200')
rescue => e
errors.add(:request, "Request failed for #{counter.name} with #{}: #{e.message}")
end
......@@ -161,14 +161,17 @@ class SIPPortal < CounterInterface
}
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
http.request(request)
end # TODO rescue from timeout, retry
#puts response
if response.code != '200'
errors.add(:request, "Request failed for #{counter.name} with #{response.code} #{response.message}
#{response.body} ")
response_body = nil
status = false
begin
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
http.request(request)
end
response_body = response.body
status = (response.code == '200')
rescue => e
errors.add(:request, "Request failed for #{counter.name} with: #{e.message}")
return '' # Early exit when error...
end
......@@ -211,6 +214,6 @@ end
# SIP data imported manually
class Offsite < CounterInterface
def download_counts(counter, date)
errors.add(:programming_code, "downloads not eligible for #{counter.name}")
end
end
module RangeChecker
# TODO: check that periods do not clash with first_active and last_active... for both branch and counter
#
# Checks if an overlapping area is counted more than once, which would be strictly verboten.
#
# The optional date_ranges parameter can be used to test whether a period can be added without
# violating the aforementioned constraint.
#
# Returns: array list of intersections
# Example param usage: [(Date.new(2018,9,1)..Date.new(2018,9,7)).to_a]
#
def get_intersecting_periods(date_ranges = [])
overlapping_counters.each do |oc|
counter = oc.counter
counter.primary_source_periods.each do |period|
period.to_date = Date.today if period.to_date.nil?
date_ranges << (period.from_date...period.to_date).to_a
end
end
intersections = []
no_of_ranges = date_ranges.size
while no_of_ranges > 1 do
current_range = date_ranges.shift
date_ranges.each do |range|
intersections << (current_range & range)
end
no_of_ranges -= 1
end
intersections.flatten.uniq
end
end
\ No newline at end of file
......@@ -224,6 +224,15 @@ class StatisticsView < ActiveRecord::Base
def self.update(item, period_types, date, has_active_oh)
period_types.each do |period|
stats = period.safe_constantize.locate_or_initialize_by(item, date)
if stats.nil?
#puts item.inspect
end
if item.id == 45
#puts item.inspect
#puts stats.inspect if stats.present?
end
stats.calculate_and_save!(has_active_oh) unless stats.nil?
end
end
......
This diff is collapsed.
......@@ -37,7 +37,7 @@ $(function() {
window.location = `${window.location.pathname}?${searchParams.toString()}`
})
$('#cancel_edit_entrances_button').click(event => {
$('.cancel_edit_entrances_button').click(event => {
const searchParams = new URLSearchParams(window.location.search)
searchParams.delete('edit_entrance_mode')
window.location = `${window.location.pathname}?${searchParams.toString()}`
......@@ -99,6 +99,21 @@ $(function() {
const selectedID = $(this).val()
const selectedName = $(this).prop('name')
// clear out url parameters whenever the selector has the 'preserveparameters' data attribute set...
// clumsy mechanism, but it works (nah, just kidding, only partly works :( )
if ($(this).data().hasOwnProperty('preserveparameters')) {
const preservationList = $(this).data('preserveparameters') ? $(this).data('preserveparameters').split(",") : []
const deleteList = []
for (const key of searchParams.keys()) {
if (preservationList && !(key == 'activeTab' || preservationList.includes(key))) {
//deleteList.push(key)
}
}
deleteList.forEach(param => searchParams.delete(param))
}
searchParams.set(selectedName, selectedID)
window.location = `${window.location.pathname}?${searchParams.toString()}`
})
......
<% is_readonly = defined?(readonly) && readonly ? "readonly" : "" %>
<% type = defined?(txt) ? txt : "" %>
<%= "<select class='asset_selector' name='#{name}'
data-navigateOnChange='#{special_option != :no_navigate}' #{is_readonly}>" %>
<% preserve = defined?(preserve) ? "data-preserveparameters=#{preserve}" : '' %>
<%= "<select class='asset_selector' name='#{name}'
data-navigateOnChange='#{special_option != :no_navigate}' #{preserve} #{is_readonly}>" %>
<%= "<option value='_all' selected>Alle #{type}</option>" if special_option == :all %>
<%= "<option value='' selected>Ny #{type}</option>" if special_option == :new %>
......
<link rel="stylesheet" href="/css/jquery-ui.min.css">
<script src="/js/jquery-ui.custom.min.js"></script>
<script src="/js/jquery-ui.multidatespicker.js"></script>
<script src="/js/manage_assets.js"></script>
<h2>Administrering</h2>
<div class="assets">
<ul class="tasktabs">
<li class="active">Grunninfo</li>
<li>Innganger</li>
<li>Tellere</li>
<li>Kalender</li>
</ul>
<hr>
<div class="form_wrapper taskpane">
<h3>Filialer</h3>
<label>Filial:</label>
<select id="branch_selector" class="branch_selector has_new_option"></select>
<form action="/adm/api/branch" method="post" autocomplete="off">
<input type="hidden" name="id">
<p>
<label>Navn:</label>
<input type="text" name="name">
</p>
<p>
<label>Filial-kode:</label>
<input type="text" name="branch_code">
</p>
<input type="submit" value="Lagre">
</form>
</div>
<div class="form_wrapper taskpane" id="closed_periods">
<h3>Stengte perioder</h3>
<label>Periode:</label>
<select id="closed_period_selector" class="closed_period_selector has_new_option"></select>
<form action="/adm/api/closed_period" method="post" autocomplete="off">
<input type="hidden" name="id">
<p>
<label>Filial:</label>
<select name="branch_id" class="branch_selector"></select>
</p>
<p>
<label>Navn:</label>
<input type="text" name="name" id="period_name">
</p>
<p>
<label>Stengt:</label>
<input type="text" class="datepicker" id="closed_on" name="closed_on">
</p>
<p>
<label>Gjenåpnet:</label>
<input type="text" class="datepicker" id="reopened_on" name="reopened_on">
</p>
<input type="submit" value="Lagre">
</form>
</div>
<div id="types" class="form_wrapper taskpane">
<h3>Typer</h3>
<p>VIKTIG: Når man har lagt til en type her, er bare halve jobben gjort. Det må i tillegg opprettes
en tabell med matchende navn i databasen, samt en klassemodell som implementerer grensesnittet for tellere.
Til slutt legger man inn en matchende tabell i erb-fila. Se dokumentasjon (når denne en dag blir ferdig) for
ytterligere informasjon.</p>
<label>Type:</label>
<select id="type_selector" class="type_selector has_new_option"></select>
<form action="/adm/api/source_type" method="post" autocomplete="off">
<input type="hidden" name="id">
<p>
<label>Navn:</label>
<input type="text" name="name">
</p>
<p><label>Beskrivelse:</label>
<textarea name="description" rows="4" cols="50"></textarea>
</p>
<input type="submit" value="Lagre">
</form>
</div>
<div id="counters" class="form_wrapper taskpane">
<h3>Tellere</h3>
<form id="counter_form">
<input type="hidden" name="source_id">
<input type="hidden" name="source_type">
<label>Teller:</label>
<select id="counter_selector" class="counter_selector has_new_option" name="id"></select>
<select class="type_selector" name="source_type_id"></select>
<select class="branch_selector" name="branch_id"></select>
<p>
<label>Aktiv</label>
<input type="radio" name="is_active" value="true">
<label>Inaktiv</label>
<input type="radio" name="is_active" value="false">
</p>
<p>
<label>Startdato:</label>
<input type="text" class="datepicker" id="start_date" name="first_active_date">
<button type="button" class="set_to_nil">Blank ut</button>
</p>
<p>
<label>Sluttdato:</label>
<input type="text" class="datepicker" id="end_date" name="last_active_date">
<button type="button" class="set_to_nil">Blank ut</button>
</p>
</form>
<div id="source_form"></div>
<!-- Hidden form templates are copied here -->
<button type="button" id="save_counter_button">Lagre</button>
</div>
</div>
<hr>
<!-- -->
<!-- Hidden form templates -->
<!-- -->
<div id="form_templates" hidden>
<div id="Camera" class="'source_form'">
<form>
<input type="hidden" name="id">
<input type="hidden" name="branch_id">
<p>
<label>Navn:</label>
<input type="text" name="name">
</p>
<p>
<label>Adresse:</label>
<input type="text" name="address">
</p>
<p>
<label>Brukernavn:</label>
<input type="text" name="user">
</p>
<p>
<label>Passord:</label>
<input type="password" name="pw">
<button type="button" class="toggleDefinition"> se</button>
</p>
</form>
</div>
<!-- -->
<!--Portal template -->
<!-- -->
<div id="form_templates" hidden>
<div id="Portal" class="'source_form'">
<form>
<input type="hidden" name="id">
<input type="hidden" name="branch_id">
<p>
<label>Navn:</label>
<input type="text" name="name">
</p>
<p>
</form>
</div>
<!-- -->
<!--Insert new templates here -->
<!-- -->
</div>
......@@ -14,11 +14,11 @@
<label>Teller:</label>
<%= erb :_selector, locals: {collection: branches, selected_id: selected_branch&.id,
name: "branch_id", special_option: nil, readonly: selected_counter.present?}%>
<%= erb :_selector, locals: {collection: entrances, selected_id: selected_entrance&.id,
name: "entrance_id" , special_option: :all, txt: "innganger", readonly: selected_counter.present?}%>
name: "branch_id", special_option: nil, readonly: selected_counter.present?, preserve: '' }%>
<%= erb :_selector, locals: {collection: entrances, selected_id: selected_entrance&.id, preserve: 'branch_id',
name: "entrance_id" , special_option: :nil, readonly: selected_counter.present?}%>
<%= erb :_selector, locals: {collection: interfaces, selected_id: selected_interface&.id,
name: "counter_interface_id" , special_option: :nil}%>
name: "counter_interface_id" , special_option: selected_counter.present? ? :no_navigate : nil} %>
<%= erb :_selector, locals: {collection: counters, selected_id: selected_counter&.id,
name: "counter_id" , special_option: :new, txt: "teller"} %>
......
<div class='form_wrapper taskpane' id='entrances'>
<p>
<label>Filial:</label>
<% special_option = edit_entrances_mode ? :new : :nil %>
<label>Inngang:</label>
<% special_option = edit_entrance_mode ? :new : :nil %>
<%= erb :_selector, locals: {collection: branches, selected_id: selected_branch&.id,
name: 'branch_id', special_option: nil, class_name: 'branch_selector' } %>
<%= erb :_selector, locals: {collection: entrances, selected_id: selected_entrance&.id,
name: 'entrance_id' , special_option: special_option, class_name: 'entrance_selector' } %>
<button id='edit_entrances_button'>Rediger/opprett inngang</button>
name: 'branch_id', special_option: nil, class_name: 'branch_selector', preserve: '' } %>
<%= erb :_selector, locals: {collection: entrances, selected_id: in_edit_entrance&.id,
name: 'entrance_id' , special_option: special_option, class_name: 'entrance_selector', preserve: 'branch_id,edit_entrance_mode' } %>
<%= edit_entrance_mode ?
"<button type='button' class='cancel_edit_entrances_button'>Avbryt redigering</button>"
: "<button id='edit_entrances_button'>Rediger inngang</button>"
%>
</p>
<ol>
<% (counters).each do |counter| %>
<%= " <li><span>#{counter.name}#{' (aktiv)' if counter.active?}
(#{counter.counter_interface.name}) #{counter.first_active_date} /
#{counter.last_active_date || '->'}
</span></li> " %>
<% end %>
</ol>
<hr>
<!------ Edit entrances ------>
<%= "<form action='/adm/api/branch/#{selected_branch&.id}/entrance'
method='post' autocomplete='off' #{'hidden' unless edit_entrances_mode}> "%>
method='post' autocomplete='off' #{'hidden' unless edit_entrance_mode}> "%>
<%= "<input type='hidden' name='id' value='#{selected_entrance&.id}'> "%>
<%= "<input type='hidden' name='id' value='#{in_edit_entrance&.id}'> "%>
<p>
<label>Navn:</label>
<input type='text' name='name' <%="value='#{selected_entrance&.name}'" %>>
<input type='text' name='name' placeholder='Ny' <%="value='#{in_edit_entrance&.name}'" %>>
</p>
<p>
<label>Startdato:</label>
<input type='text' class='datepicker' name='first_active_date'
<%="value='#{selected_entrance&.first_active_date}'"%>>
<%="value='#{in_edit_entrance&.first_active_date}'"%>>
</p>
<p>
<label>Sluttdato:</label>
<input type='text' class='datepicker' name='last_active_date'
<%="value='#{selected_entrance&.last_active_date}'"%>>
<%="value='#{in_edit_entrance&.last_active_date}'"%>>
<button type='button' class='set_to_nil'>Blank ut dato</button>
</p>
<p>
<label>Beskrivelse:</label>
<textarea name='description' rows='5' cols='60'><%="#{selected_entrance&.description}"%></textarea>
<textarea name='description' rows='5' cols='60'><%="#{in_edit_entrance&.description}"%></textarea>
</p>
<label></label><input type='submit' style='width:200px;' value='<%= selected_entrance.present? ? "Lagre endringer" : "Opprett ny" %>'>
<button type="button" id="cancel_edit_entrances_button">Avbryt</button>
<label></label><input type='submit' style='width:200px;' value='<%= in_edit_entrance.present? ? "Lagre endringer" : "Opprett ny" %>'>
<button type="button" class="cancel_edit_entrances_button">Avbryt</button>
</form>
<!------ Handle primary source periods ------>
<h3>Primærkilder</h3>
<% if selected_entrance&.missing_dates? %>
<% missing_date_ranges = selected_entrance&.missing_date_ranges %>
<%= "<input hidden id='entrance_id' value='#{selected_entrance&.id}'> " %>
<h3>Primærkilder</h3>
<% if (selected_entrance.missing_dates?) %>
<% missing_date_ranges = selected_entrance.missing_date_ranges %>
<%= "<input hidden id='entrance_id' value='#{selected_entrance.id}'> " %>
<hr>
<h4 style="color:red">Manglende dekning!</h4>
<p>Inngangen mangler dekning for følgende periode(r). Husk å sette dirty_bit. Husk å fikse
logikken for å endre filial for teller...</p>
<p>Inngangen mangler dekning for følgende periode(r).</p>
<% missing_date_ranges.each do |range| %>
<div>
<%= "<form action='/adm/api/entrances/#{selected_entrance&a