@CAMP dev team
incorporate the self study layer in Camp, to make student experience exactly the same as in study plan, but...we're not the school team
topic
N/A
week
task
step
activity
course
level
unit
lesson
step
activity
topic
N/A
week
task
step
activity
course
level
unit
lesson
step
activity
/school/e12/#school/2262345/385/550/2074/8861/2312/116439
topic
N/A
week
task
step
activity
course
level
unit
lesson
step
activity
/camp/study#/study/YGyu5kxSRvyOgmxwaU9a2w/%3DRjO65LOSDulAgHXO1Kmig/c3%2BCxfd1TOaV4gCXdABIeA/IvltDfODS1usiBedyKPYRA
/camp/study#/study/YGyu5kxSRvyOgmxwaU9a2w/%3DRjO65LOSDulAgHXO1Kmig/c3%2BCxfd1TOaV4gCXdABIeA/IvltDfODS1usiBedyKPYRA
/school/e12/#school/2262345/385/550/2074/8861/2312/116439
#/study/YGyu.../
#/study/YGyu.../%3D.../
#/study/YGyu.../%3D.../n9W.../
#/study/...*.../eOrulp.../
which camp
which camp week
scroll into a camp task
open activity container of one task step
Efecta 13 is definitely a legacy application written in 4 years ago on troopjs 1.0 and is a bounch of jQuery plugin soup
Thanks to troop Efecta 13 is a requireJS application completely driven by hub events
A significant advantage of using hub is components can inject themselves in processing flow without modifying the host application, this allows for swapping out implementations of components without having to update all the dependent applications.
{id: "activity!142748" …}
load/result
Cassandra is perfect for managing large amounts of data across multiple data centers and the cloud. Cassandra delivers continuous availability, linear scalability, and operational simplicity across many commodity servers with no single point of failure, along with a powerful data model designed for maximum flexibility and fast response times.
CREATE TABLE Class_Tasks(
Class_Id uuid,
Class_Section_Id uuid,
Class_Task_Id uuid,
Task_Id uuid,
Task_Order int,
Task_Type text,
Task_Sub_Type text,
Name text,
Description text,
Start_Time text,
End_Time text,
Insert_Date timestamp,
Update_Date timestamp,
PRIMARY KEY(
Class_Id,
Class_Section_Id,
Class_Task_Id
)
);
CREATE TABLE Class_By_Task(
Class_Task_Id uuid,
Class_Section_Id uuid,
Class_Id uuid,
Insert_Date timestamp,
Update_Date timestamp,
PRIMARY KEY (
Class_Task_Id
)
);
The unique combination of Cassandra and DSE Search with Solr features robust full-text search, hit highlighting, and rich document (PDF, Microsoft Word, etc) handling.
create table group_lesson_feedback(
class_id uuid,
class_task_id uuid,
comment text,
student_status text,
teacher_member_id int,
closed boolean,
notified boolean,
prop_ map<text, text>,
insert_date timestamp,
update_date timestamp,
PRIMARY KEY(
class_id,
class_task_id
)
);
e.g. the column value of student_status:
[
{
"memberid": 1234,
"absent": "true",
"techissue": "false"
},
{
"memberid": 56789,
"absent": "false",
"techissue": "false"
}
]
e.g. the column value of prop_ map:
{
'prop_name1': 'value1',
'prop_name2': 'value2'
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema name="autoSolrSchema" version="1.5">
<types>
<fieldType class="org.apache.solr.schema.TextField" name="TextField">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<fieldType class="org.apache.solr.schema.TrieIntField" name="TrieIntField"/>
<fieldType class="org.apache.solr.schema.UUIDField" name="UUIDField"/>
<fieldType class="org.apache.solr.schema.BoolField" name="BoolField"/>
<fieldType class="org.apache.solr.schema.TrieDateField" name="TrieDateField"/>
</types>
<fields>
<dynamicField indexed="true" multiValued="false" name="prop_*" stored="true" type="TextField"/>
<field indexed="true" multiValued="false" name="class_id" stored="true" type="UUIDField"/>
<field indexed="true" multiValued="false" name="class_task_id" stored="true" type="UUIDField"/>
<field indexed="true" multiValued="false" name="closed" stored="true" type="BoolField"/>
<field indexed="true" multiValued="false" name="notified" stored="true" type="BoolField"/>
<field indexed="true" multiValued="false" name="teacher_member_id" stored="true" type="TrieIntField"/>
</fields>
<uniqueKey>class_id</uniqueKey>
</schema>
--e.g. query by any column configured in solr schema
select comment, student_status, closed, prop_
from group_lesson_feedback
where solr_query = 'teacher_member_id:12345678';
--e.g. query by dynamic column "prop_"
select comment, student_status, closed, prop_
from group_lesson_feedback
where solr_query = 'prop_name2:value2';
--e.g. Camp key_space on live
CREATE KEYSPACE IF NOT EXISTS Camp
WITH REPLICATION = {
'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy',
'US1': 3,
'CN1': 3
};
There must be a formalized way of describing application APIs, before and after they are implemented, this reduce significantly the communication cost between front-back-end and reinforces API robustness and stablization.
the Swagger Schema allows the definition of data type expectations, this schema is based on the JSON Schema Specification Draft 4, and uses a predefined subset of it.
Rethink of the best practices to deliverUIs influenced by the movie producing industry.
I'm waiting for the whole page to reload, just to test that little change.
The server API required for testing this feature is not yet ready.
My team have no idea of what the final UI looks until 3 weeks later.
Trying to understand how to apply individual DOM manipulation smartly enough
to avoid re-rendering for each scenario,
is an super insufficient as well as error prune procedure
dom mutation is the most expensive part of the browser, performance wise application should avoid unnecessary DOM updates to but only to carry the least changes required.
{{!--camp widget --}}
{{#each camp.sections}}
<div data-weave="widget/section(section)" data-section={{JSON.stringify(this)}}></div>
{{/each}}
{{!--section widget --}}
{{#each section.tasks}}
<div data-weave="widget/task(task)" data-task={{JSON.stringify(this)}}></div>
{{/each}}
{{!--task widget --}}
{{#each task.steps}}
<div data-weave="widget/step(step)" data-step={{JSON.stringify(this)}}></div>
{{/each}}
recursively render the camp data
render each section
render each task
render each step
when "step" data changes on camp
?
?
?
/* camp.jsx */
return camp.sections.map(function(section) {
return <Section section={section} />;
});
/* section.jsx */
return section.tasks.map(function(task) {
return <Task task={task} />;
});
/* task.jsx */
return task.steps.map(function(step) {
return <Step step={step} />;
});
when "step" data changes on camp
update each section
update each task
step is updated!
Re-render the whole app on each update, let the framework to remember which widget is to create or to update
For the most productive frontiers, local development shall requires no server environment setup at all, yet remains as close as possible to all production environments, aka. load from HTML
#/usr/local/etc/dnsmasq.conf
local=/localhost/
address=/dev/127.0.0.1
# CAMP QA Reverse Proxy
<VirtualHost *:80>
DocumentRoot /Users/garry/Stash/camp-ui
ServerName qa.dev
ProxyPassMatch ^/(.*\.html)$ http://localhost:3000/$1
ProxyPass /camp/login http://qa.englishtown.com/camp/login
ProxyPass /camp/enroll http://qa.englishtown.com/camp/enroll
ProxyPass /camp !
ProxyPass / http://qa.englishtown.com/
ProxyPassReverse / http://qa.englishtown.com/
ProxyPassReverseCookieDomain qa.englishtown.com qa.dev
</VirtualHost>
<!doctype html>
<html lang="en">
<head>
<title>Englishtown | CAMP | Study </title>
<!-- build:css css/camp.css -->
<link rel="stylesheet" href="icons/style.css"/>
<link rel="stylesheet" href="css/camp.css"/>
<!-- endbuild -->
getContent::Camp_common-head
<!-- build:js camp.js -->
<script src="bower_components/requirejs/require.js"></script>
<script src="common-camp.js"></script>
<!-- endbuild -->
getContent::Camp_common-tail
</head>
<body class="ets-spinning-global" data-weave="camp-ui/widget/layouts/main">
<div class="spinner"></div>
</body>
</html>
CMS is a good point, but I always miss the developing experience from local files and the safety have everything version controlled in Git
We have escaped from Umbraco
actually we used that exclusively:
Combine the best from both worlds
npm install -g umbra
18:50 $ umbra -h
Usage: umbra [options] [command]
Commands:
push [files...] publish specific file(s) or the entire "cms" directory to Umbraco
pull fetch newly created Umbraco contents back to the "cms" directory
CLI for publish Umbraco CMS contents from local files
Options:
-h, --help output usage information
-V, --version output the version number
09:24 $ ll cms/*landing*.html
-rw-r--r--+ 1 garry staff 9119 May 7 17:31 cms/Camp_landing-cs.html
-rw-r--r--+ 1 garry staff 9781 May 7 17:31 cms/Camp_landing-en.html
-rw-r--r--+ 1 garry staff 773 May 7 17:31 cms/Camp_landing-navbar-cs.html
-rw-r--r--+ 1 garry staff 767 May 7 17:31 cms/Camp_landing-navbar-en.html
-rw-r--r--+ 1 garry staff 792 Apr 26 17:21 cms/Camp_landing-navbar-topic-ji-cs.html
-rw-r--r--+ 1 garry staff 794 Apr 26 17:21 cms/Camp_landing-navbar-topic-ji-en.html
-rw-r--r--+ 1 garry staff 15103 May 7 17:31 cms/Camp_landing-topic-ji-cs.html
-rw-r--r--+ 1 garry staff 16307 May 7 17:31 cms/Camp_landing-topic-ji-en.html
09:24 $ umbra push cms/*landing*.html
[ok] camp_landing-en (31744) has been published as "getContent::Camp_landing-navbar-en
<d..."
[ok] camp_landing-navbar-topic-ji-en (33202) has been published as "<nav class="navbar navbar-default nav..."
[ok] camp_landing-topic-ji-cs (31750) has been published as "getContent::Camp_landing-navbar-topic..."
[ok] camp_landing-navbar-cs (33199) has been published as "<nav class="navbar navbar-default nav..."
[ok] camp_landing-cs (31748) has been published as "getContent::Camp_landing-navbar-cs
<d..."
[ok] camp_landing-navbar-topic-ji-cs (33201) has been published as "<nav class="navbar navbar-default nav..."
[ok] camp_landing-navbar-en (33197) has been published as "<nav class="navbar navbar-default nav..."
[ok] camp_landing-topic-ji-en (31751) has been published as "getContent::Camp_landing-navbar-topic..."
pre-recieve
post-recieve
update
no developer setup
requires server-side (Stash) support
less error prune, more accurate
git hook? | git diff-tree $ref head | grep "cms" | umbraco push
Umbraco publishing in a pipe
github web hook | http listener | git fetch |
git diff-tree $ref team/virus/develop |
grep "cms" | umbraco push
Base on Flash
<meta name="renderer" content="webkit">
Live Error: Fails to upload recording: 500
Live Error: Fails to upload recording: 500
Automation test
Raygun