Skip to content

Commit 3e49dd7

Browse files
committed
Adding CalendarSignage. This would potentially be a good place to fork that file for other purposes.
1 parent 161bebd commit 3e49dd7

1 file changed

Lines changed: 292 additions & 0 deletions

File tree

Calendar/CalendarSignage.py

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
global q, model, Data
2+
3+
def get_reservations_for_room(reservableIds, startDate, days):
4+
5+
if not isinstance(reservableIds, list):
6+
reservableIds = [reservableIds]
7+
8+
query = '''
9+
SELECT
10+
rb.ReservableId,
11+
rv.MeetingStart,
12+
COALESCE(rv.MeetingEnd, rv.MeetingStart) as MeetingEnd,
13+
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as SetupMinutes,
14+
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as TeardownMinutes,
15+
COALESCE(NULLIF(m.Description, ''), o.OrganizationName) as Name,
16+
0 as Quantity,
17+
m.MeetingId,
18+
o.LeaderName
19+
INTO #reservables
20+
FROM Reservations rv
21+
JOIN Reservable rb ON rv.ReservableId = rb.ReservableId
22+
JOIN Meetings m ON rv.MeetingId = m.MeetingId
23+
JOIN Organizations o ON m.OrganizationId = o.OrganizationId
24+
WHERE rv.MeetingId IS NOT NULL
25+
AND rv.MeetingEnd > '{0}'
26+
AND rv.MeetingStart < DATEADD(day, 1, '{0}');
27+
'''.format(reservableIds)
28+
29+
30+
def get_reservables(typ):
31+
return q.QuerySql("""
32+
SELECT ReservableId,
33+
ParentId,
34+
COALESCE(BadgeColor, '#153aa8') as Color,
35+
Name,
36+
Description,
37+
IsReservable,
38+
IsEnabled,
39+
IsCountable,
40+
Quantity
41+
FROM Reservable
42+
WHERE 1=1
43+
AND ReservableTypeId = {}
44+
AND IsDeleted = 0
45+
AND IsEnabled = 1
46+
47+
ORDER BY ReservableTypeId, Name
48+
;
49+
""".format(typ))
50+
51+
if model.HttpMethod == "post" and Data.v == "saveSigns":
52+
# save signage configuration
53+
import json
54+
55+
rawData = Data.data
56+
try:
57+
signageData = json.loads(rawData)
58+
model.WriteContentText("CalendarSignageConfiguration.json", json.dumps(signageData), "CalendarSignage")
59+
print(json.dumps({"status": "success"}))
60+
except Exception as e:
61+
model.Log("Error parsing signage data: {}".format(str(e)))
62+
print(json.dumps({"error": "data parsing error"}))
63+
64+
65+
66+
elif Data.v == "":
67+
# configuration mode
68+
69+
# import knockout from cdn
70+
# language=html
71+
model.Title = "Calendar Signage Configuration"
72+
73+
# noinspection PyBroadException
74+
try:
75+
originalConfig = model.Content("CalendarSignageConfiguration.json")
76+
except:
77+
print("<p>Caution: could not load existing configuration, starting fresh.</p>")
78+
originalConfig = None
79+
80+
if originalConfig is None or originalConfig.strip() == "":
81+
originalConfig = "[]"
82+
83+
ko_form = """
84+
<script>
85+
const originalData = {};
86+
</script>
87+
""".format(originalConfig)
88+
89+
# Form that lists signs in the system
90+
# language=html
91+
ko_form = ko_form + """
92+
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
93+
94+
<table style="border-collapse: collapse; width: 100%;" data-bind="visible: signs().length > 0">
95+
<thead>
96+
<tr>
97+
<th>Name</th>
98+
<th>Sign Template</th>
99+
<th>Print Copies</th>
100+
<th>Items</th>
101+
<th>Options</th>
102+
</tr>
103+
</thead>
104+
<tbody data-bind="foreach: signs">
105+
<tr>
106+
<td><input data-bind="value: name"/></td>
107+
<td><select
108+
data-bind="options: $parent.types, optionsText: 'name', optionsValue: 'fileName', value: type">
109+
</td>
110+
<td><input type="number" data-bind="value: copies" min="0"/></td>
111+
<td><a data-bind="text: items().length, click: $parent.editSign"></a></td>
112+
<td>
113+
<a data-bind="attr: { href: previewLink }">Preview</a>
114+
<a data-bind="click: $parent.removeSign">Remove</a>
115+
</td>
116+
</tr>
117+
</tbody>
118+
</table>
119+
<button data-bind="click: addSign">Add Sign</button>
120+
121+
122+
<div class="modal" id="edit-sign-dialog" data-keyboard="false" data-backdrop="static" style="background: #0009;">
123+
<div class="modal-dialog modal-md" data-bind="if: currentlyEditingSign() !== null">
124+
<div class="modal-content">
125+
<div class="modal-header">
126+
<button type="button" class="close" data-dismiss="modal" data-bind="click: closeModal" aria-label="Close"><span aria-hidden="true">×</span></button>
127+
<h4 class="modal-title" data-bind="text: 'Editing ' + currentlyEditingSign().name()"></h4>
128+
</div>
129+
<div class="modal-body">
130+
<div class="form-group">
131+
<label for="OrganizationName" class="control-label">Name</label>
132+
<input class="form-control" id="org_OrganizationName" name="org.OrganizationName" type="text" value="">
133+
</div>
134+
<div class="form-group">
135+
<label for="InvolvementType" class="control-label">Involvement Type</label>
136+
<select class="form-control" data-val="true" data-val-number="The field Organization Type must be a number." id="org_OrganizationTypeId" name="org.OrganizationTypeId"><option value="0">(not specified)</option>
137+
<option value="2">Children/Youth Program</option>
138+
<option value="10">Small Group</option>
139+
<option value="20">Adult Class</option>
140+
<option value="40">Missions Trip</option>
141+
<option value="49">Job Application</option>
142+
<option selected="selected" value="50">Administration</option>
143+
<option value="51">Parish Council</option>
144+
<option value="70">Social Event</option>
145+
<option value="75">Conference</option>
146+
<option value="81">Worship Services</option>
147+
<option value="91">Communications</option>
148+
<option value="92">Mobile App</option>
149+
</select>
150+
</div>
151+
</div>
152+
</div>
153+
</div>
154+
</div>
155+
156+
157+
158+
159+
<script>
160+
function Sign(data) {
161+
const that = this;
162+
this.name = ko.observable(data.name);
163+
this.items = ko.observableArray(data.items || []);
164+
this.copies = ko.observable(data.copies || 1);
165+
this.type = ko.observable(data.type || "CalendarSignageDirectional");
166+
this.id = ko.observable(data.id || btoa(Math.random().toString()).substring(0, 6));
167+
this.previewLink = ko.computed(function () {
168+
return "?v=" + that.id();
169+
});
170+
171+
this.copies.subscribe(() => window.signageViewModel.saveSigns());
172+
this.name.subscribe(() => window.signageViewModel.saveSigns());
173+
this.type.subscribe(() => window.signageViewModel.saveSigns());
174+
this.items.subscribe(() => window.signageViewModel.saveSigns());
175+
176+
this.toJson = function () {
177+
return {
178+
name: that.name(),
179+
items: that.items(),
180+
copies: that.copies(),
181+
type: that.type(),
182+
id: that.id()
183+
};
184+
};
185+
}
186+
187+
function ViewModel(data) {
188+
const self = this;
189+
self.signs = ko.observableArray([]);
190+
191+
data.forEach(function (signData) {
192+
self.signs.push(new Sign(signData));
193+
});
194+
195+
self.types = ko.observableArray([
196+
{name: "Directional", fileName: "CalendarSignageDirectional"},
197+
{name: "Reservations", fileName: "CalendarSignageReservations"},
198+
{name: "Event Details", fileName: "CalendarSignageEventDetails"}
199+
]);
200+
201+
self.currentlyEditingSign = ko.observable(null);
202+
203+
self.currentlyEditingSign.subscribe(() => {
204+
console.log("here");
205+
console.log(self.currentlyEditingSign())
206+
if (self.currentlyEditingSign() !== null) {
207+
$('#edit-sign-dialog').fadeIn();
208+
} else {
209+
$('#edit-sign-dialog').fadeOut();
210+
}
211+
});
212+
213+
self.closeModal = function () {
214+
self.currentlyEditingSign(null);
215+
}
216+
217+
self.addSign = function () {
218+
const newSign = new Sign({name: "New Sign"});
219+
self.signs.push(newSign);
220+
return newSign;
221+
};
222+
223+
self.editSign = function (sign) {
224+
self.currentlyEditingSign(sign);
225+
};
226+
227+
self.removeSign = function (sign) {
228+
self.signs.remove(sign);
229+
};
230+
231+
self.toJson = function () {
232+
return ko.toJS(self.signs().map(s => s.toJson()));
233+
};
234+
235+
self.saveSigns = function () {
236+
// Save signs to server
237+
const formData = new URLSearchParams();
238+
formData.append('data', JSON.stringify(self.toJson()));
239+
240+
// post to ?v=saveSigns
241+
fetch("/PyScriptForm/CalendarSignage?v=saveSigns", {
242+
method: "POST",
243+
credentials: 'include',
244+
body: formData
245+
}).then(response => {
246+
if (!response.ok) {
247+
throw new Error("Network response was not ok");
248+
}
249+
return response.json();
250+
}).then(() => {
251+
console.log("Signs saved successfully!");
252+
})
253+
};
254+
255+
self.signs.subscribe(function (newSigns) {
256+
// Save signs to server on change
257+
self.saveSigns()
258+
});
259+
}
260+
261+
window.signageViewModel = new ViewModel(originalData);
262+
ko.applyBindings(window.signageViewModel);
263+
</script>
264+
"""
265+
266+
print(ko_form)
267+
268+
269+
# print("<p>Select Items to print signage for</p>")
270+
# print("<table>")
271+
# for i in [(1, "Rooms"), (2, "Jawns"), (3, "Other Jawns")]:
272+
# print("<tr><td colspan=\"2\"><h2>{}</h2></td></tr>".format(i[1]))
273+
# l = get_reservables(i[0])
274+
# if l:
275+
# for r in l:
276+
# print("""
277+
# <tr>
278+
# <td><input type="checkbox" id="{1}" /></td>
279+
# <td><label for="{1}">{0}</label></td>
280+
# """.format(r.Name, r.ReservableId))
281+
#
282+
# if i[0] == 1:
283+
# print("""
284+
# <td><input type="checkbox" id="children-{1}" /></td>
285+
# """.format(r.Name, r.ReservableId))
286+
#
287+
# else:
288+
# print("<td></td>")
289+
#
290+
# print("</tr>")
291+
# print("</table>")
292+
#

0 commit comments

Comments
 (0)