Divide rewritten documentation into separate chapters
parent
abec266d97
commit
6c5736f7ea
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,88 @@
|
|||||||
|
# Conclusion
|
||||||
|
|
||||||
|
The project of ReCodEx was a great experience with developing a bigger
|
||||||
|
application in team of people. We mostly enjoyed the time spent on the project,
|
||||||
|
however the deadline was too tight to implement all nice features we thought of.
|
||||||
|
Our implementation meets our predefined goals and is ready to be deployed
|
||||||
|
instead of current tool CodEx.
|
||||||
|
|
||||||
|
We made several design choices during planning and implementing the project.
|
||||||
|
From perspective of today we are mostly happy about them and we believe we have
|
||||||
|
done the best we could. One of the biggest questions was the broker/worker
|
||||||
|
programming language. The winner was C++, which is really great language
|
||||||
|
especially in revisions starting at C++11. But we are not sure if Python would
|
||||||
|
not have been better option. From the list of good choices we would like to
|
||||||
|
mention early unit testing with continuous integration, using ZeroMQ messaging
|
||||||
|
framework and the design with splitting logic into multiple components.
|
||||||
|
|
||||||
|
To sum up, we created an extensible base environment for years of further
|
||||||
|
development by numerous student projects. The faculty would benefit from higher
|
||||||
|
hardware utilization, less administration effort and a possibility to offer
|
||||||
|
ReCodEx as SaaS to partner grammar schools. Students would also appreciate
|
||||||
|
state-of-art development tools.
|
||||||
|
|
||||||
|
During the development we were using whole bunch of development tools. We would
|
||||||
|
like to thank authors, maintainers and contributors of these tools to help us
|
||||||
|
complete this project. Most notably we would like to mention explicitly the top
|
||||||
|
projects: Git and GitHub, Visual Studio Code, IDEs from JetBrains, ZeroMQ
|
||||||
|
framework, C++ spdlog library, Travis and AppVeyor CI, Nette framework and many
|
||||||
|
many others. Thank you.
|
||||||
|
|
||||||
|
Finally, we would like to thank our supervisor, Martin Kruliš, for his support
|
||||||
|
and great effort correcting text of this documentation.
|
||||||
|
|
||||||
|
## Further Improvements
|
||||||
|
|
||||||
|
A lot of work has been done, but it opened a whole bunch of new possibilities
|
||||||
|
for subsequent student projects and other kinds of contribution. We would be
|
||||||
|
happy to see people contributing to this project to make it even more awesome.
|
||||||
|
We are presenting a brief list of features we think might be worth of
|
||||||
|
completing. Surely, the list is not complete and may change in time.
|
||||||
|
|
||||||
|
- Finish web frontend. In time of the project submission it does not contain all
|
||||||
|
features for the best user experience in all roles. This task is suitable for
|
||||||
|
people with basic web programming experience, React and Redux knowledge is an
|
||||||
|
advantage.
|
||||||
|
- Create web editor of job configuration. ReCodEx job evaluation follows quite
|
||||||
|
complex task flow. To create valid configuration, author of exercise has to
|
||||||
|
write a pretty long YAML file by hand. This of course can be partially or
|
||||||
|
fully automated but was not implemented at time. This task therefore consist
|
||||||
|
of creating web based editor of this configuration with prepared task
|
||||||
|
templates allowing user to change their properties and connect them. After
|
||||||
|
submit, YAML job configuration should be properly created.
|
||||||
|
- Write alternative commandline frontend. A lot of users want to submit their
|
||||||
|
solutions directly from command line. That is newly possible in ReCodEx
|
||||||
|
because of REST API, but no suitable frontend exists yet. There is unfinished
|
||||||
|
attempt to create one in NodeJS, accessible in ReCodEx organization on
|
||||||
|
[GitHub](https://github.com/ReCodEx/cli). The goal is to finish this project
|
||||||
|
or create alternative tool. We would like to see one written in Python.
|
||||||
|
- Create mobile frontend. It would be really nice to have mobile application
|
||||||
|
when you can read exercise assignments and examine your progress and
|
||||||
|
statistics. All is possible due to ReCodEx REST API. The basic Android
|
||||||
|
application is in development by ReCodEx team as project for programming
|
||||||
|
mobile devices class. The code will be published as part of ReCodEx GitHub
|
||||||
|
organization.
|
||||||
|
- Design and implement backend monitoring. For administrator would be great to
|
||||||
|
examine status of backend (broker, workers, monitor, fileserver) directly in
|
||||||
|
browser. Key information are which machines are online, offline or failed,
|
||||||
|
which worker is now running a job (maybe with current progress) and view
|
||||||
|
configuration of worker. Also some statistics with graphs should be made, for
|
||||||
|
example workload of workers. More advanced feature is the ability to restart
|
||||||
|
workers on the change of their configurations.
|
||||||
|
- Finish .NET sandbox for Windows. We developed initial sandbox for Windows
|
||||||
|
environment, [WrapSharp](https://github.com/ReCodEx/wrapsharp). WrapSharp is
|
||||||
|
only for .NET platform assemblies and most notably for C# programs and cannot
|
||||||
|
be used generally for sandboxing on Windows. The goal is to finish
|
||||||
|
implementation and do a really detailed security audit of the project. Also,
|
||||||
|
integration to the worker is not fully done yet.
|
||||||
|
- SIS integration. A very nice feature is (semi)automatic creation of groups and
|
||||||
|
assigning students to them depending on their timetable in student information
|
||||||
|
system (SIS). However there is no standardized API for such communication yet,
|
||||||
|
but we hope this will be possible in the future. Implementing this feature
|
||||||
|
means extending ReCodEx API by a SIS module in PHP.
|
||||||
|
|
||||||
|
|
||||||
|
<!---
|
||||||
|
// vim: set formatoptions=tqn flp+=\\\|^\\*\\s* textwidth=80 colorcolumn=+1:
|
||||||
|
-->
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
|||||||
# Submission flow
|
|
||||||
|
|
||||||
This article will describe in execution flow of submission from the point of submission into web application to the point of evaluation of results from execution. Note that following text is simplified and should only show concepts which are used during execution.
|
|
||||||
|
|
||||||
## Web Application
|
|
||||||
|
|
||||||
First thing users have to do is to submit their solutions to web application which provides interface to upload multiple files and then submit them. More detailed description follows:
|
|
||||||
|
|
||||||
1. user upload file by file into prepared submit form
|
|
||||||
2. after uploading all files connected to assignment user clicks on submit button
|
|
||||||
3. Web Application send request to Web API that user wants to evaluate assignment with provided files
|
|
||||||
|
|
||||||
## Web API
|
|
||||||
|
|
||||||
After user submits solution then web application has to hand over all needed information about submission to broker. Submit endpoint is extended with support of "cross-submitting" which means that user with high privileges (supervisor, administrator) can submit solution for user with lower privileges (student). More detailed description follows:
|
|
||||||
|
|
||||||
1. deadlines and count of submissions are checked
|
|
||||||
2. programming language of uploaded files is automatically detected
|
|
||||||
3. based on programming language proper job configuration file is obtained
|
|
||||||
4. submitted files from user and job configuration are uploaded to fileserver as archive
|
|
||||||
5. send message to broker that there is new submission
|
|
||||||
6. several information about new submission including websocket channel are sent back to user
|
|
||||||
|
|
||||||
## Broker
|
|
||||||
|
|
||||||
Broker gets information about new submission from API server. At this point broker has to find suitable worker for execution of this particular submission. When worker is found and is jobless, then broker send detailed submission to worker to evaluation. More detailed description follows:
|
|
||||||
|
|
||||||
1. broker gets message from API about new submission
|
|
||||||
2. based on information from API, broker has to choose suitable worker which matches all submission criteria
|
|
||||||
1. if suitable worker is not found then broker notices Web API about this information and all execution stops
|
|
||||||
2. suitable worker is found
|
|
||||||
1. worker has no jobs at the moment, broker immediately sends job evaluation request to it
|
|
||||||
2. otherwise evaluation request is queued to this particular worker and waits until all previous jobs are done
|
|
||||||
3. broker sends prepared evaluation request to worker with all information which was obtained from API
|
|
||||||
|
|
||||||
## Worker
|
|
||||||
|
|
||||||
Worker gets request from broker to evaluate particular submission. Next step is to evaluate given submission and upload results to fileserver. After this worker only send broker that submission was evaluated. More detailed description follows:
|
|
||||||
|
|
||||||
1. worker gets evaluation request from broker
|
|
||||||
2. worker now has to do some initialization of directories and some internal structures for new incoming evaluation
|
|
||||||
3. users submission archive from given address from evaluation request is downloaded and decompressed
|
|
||||||
4. job configuration file is located and parsed into internal job structure
|
|
||||||
5. tasks which are present in configuration are loaded into tree like structure with dependencies and are divided into internal or external ones
|
|
||||||
1. internal tasks are tasks which has no defined limits and contains some internal action
|
|
||||||
2. external tasks have defined limits in configuration and are executed in sandbox
|
|
||||||
6. last step of initializing of job is to topologically sort tasks and prepare queue with right execution order
|
|
||||||
7. after that topologically sorted execution queue is processed
|
|
||||||
1. if execution of `inner` task fails worker will immediately stop execution and send back to broker 'internal error during execution' message
|
|
||||||
2. execution of `execution` or `evaluation` task fails then worker stops and send back to broker 'execution failed' message
|
|
||||||
3. during execution worker can optionally sends current job status back to web application, this communication is described in more detailed [[communication|Overall architecture#communication]] article.
|
|
||||||
8. when execution successfully ends results are collected from all executed tasks and written into yaml file
|
|
||||||
9. results yaml file alongside with content of job result folder is sent back to fileserver in compressed form
|
|
||||||
10. of course there has to be some cleaning after whole evaluation which will mostly delete content of temporarily created folders
|
|
||||||
11. last step of worker is to send back to broker message that execution successfully ended
|
|
||||||
|
|
||||||
## Broker
|
|
||||||
|
|
||||||
Broker gets done message from worker and basically only mark submission as done in its internal structures. After that broker has to tell Web API that execution of particular job ended. More detailed description follows:
|
|
||||||
|
|
||||||
1. broker gets message from worker that execution of job ended
|
|
||||||
2. job execution on worker was successful
|
|
||||||
1. Web API is notified that job ended successfully
|
|
||||||
2. if there are some waiting jobs for worker than the first one is sent for execution
|
|
||||||
3. job execution on worker ended with internal error
|
|
||||||
1. if job ends with internal error than it is possible to reassign it to another worker
|
|
||||||
2. broker keeps track of job reassignments and if number of them reach some predefined constant, job is declared as failed
|
|
||||||
3. suitable worker different that the original one is picked and evaluation request is sent to it
|
|
||||||
4. job execution on worker failed
|
|
||||||
1. Web API is notified that execution of this particular job failed
|
|
||||||
2. again if there is waiting job for execution on worker than it is sent for execution
|
|
||||||
|
|
||||||
## Web API
|
|
||||||
|
|
||||||
Web API is notified about job status from broker. After that API is deciding if evaluated submission will be evaluated immediately or on demand. But for the sake of simplicity we will consider only immediately evaluated results. More detailed description follows:
|
|
||||||
|
|
||||||
1. job arrived with successful evaluation status
|
|
||||||
1. results of evaluation are downloaded from fileserver where it was uploaded by worker and then unzipped
|
|
||||||
2. result yaml file is loaded and actual time and memory consumption are retrieved
|
|
||||||
3. overall results are computed and stored into database
|
|
||||||
2. job arrived as failed
|
|
||||||
1. job failure is saved into database alongside other reported errors
|
|
||||||
2 email is sent to ReCodEx administrator that job failed and cannot be properly executed
|
|
||||||
|
|
||||||
## Web Application
|
|
||||||
|
|
||||||
Web application has only a simple work to do. It has to obtain results from web API. More detailed description follows:
|
|
||||||
|
|
||||||
1. web application sends request for results of particular submission
|
|
||||||
2. if results are available then they are sent back to web application and displayed to user
|
|
@ -1,90 +0,0 @@
|
|||||||
# Web API
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
The Web API provides a controlled access to the evaluation backend. It also
|
|
||||||
enables the use of different user frontends such as default web application,
|
|
||||||
mobile applications, commandline tools and possibly others. The communication
|
|
||||||
goes as HTTP(S) requests in predefined format, nowadays mostly known as
|
|
||||||
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) format.
|
|
||||||
Results from the API are in plain text in JSON format to be easily parsed in
|
|
||||||
various languages (notably JavaScript).
|
|
||||||
|
|
||||||
This component must be publicly visible on the internet, so it is important to
|
|
||||||
care about security and follow our recommendations. Security and user access
|
|
||||||
restriction are among our primary concerns, so proper roles with permission
|
|
||||||
separation are introduced and maintained. Also some additional checks are made
|
|
||||||
directly in the code, so that a user cannot access information which are out of
|
|
||||||
their authorization.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
Web API is written in PHP using [Nette framework](https://nette.org/en/). This framework provides useful components like _Tracy_ for logging and showing errors, _Tester_ for productive unit testing or _Latte_ templating engine. Nette is modern, widely used and great performing software with active developers and user community. Nette can help eliminate security holes, simplify debugging and make coding easier with numerous plugins and extensions. Also, it is published under permissive BSD license.
|
|
||||||
|
|
||||||
API architecture consists of several parts:
|
|
||||||
|
|
||||||
- **router** -- component handling mapping from URL addresses to methods in presenter classes (called endpoints)
|
|
||||||
- **presenters** -- classes containing one method per endpoint responsible for
|
|
||||||
fetching and parsing request arguments and performing desired actions
|
|
||||||
- **entities** -- classes persisted using a database with an ORM framework
|
|
||||||
- **repositories** -- common operations on entities of one type, mostly finding entity by identifier or persisting changes to the database
|
|
||||||
- **helpers** -- set of classes solving more complicated internal logic, used from presenters to keep them reasonably small
|
|
||||||
|
|
||||||
Each presenter method has several annotations. They are used for generating REST API documentation in [Swagger](http://swagger.io/), specifying request type and its parameters and specifying one level of access restrictions. Also, there is simple description of the endpoint.
|
|
||||||
|
|
||||||
For specifying the request type (_GET_, _POST_, _DELETE_) annotations with exactly these names without any parameters are used. To describe request parameters `@Param` annotation is used with following arguments:
|
|
||||||
|
|
||||||
- type -- the type of argument, one of _post_ of _query_
|
|
||||||
- name -- name of the argument (the key)
|
|
||||||
- validation -- validation of the value, see [Nette validation rules](https://doc.nette.org/en/2.4/validators#toc-rules)
|
|
||||||
- msg -- description for users about the values this parameter can contain
|
|
||||||
- required -- specifies if this option is mandatory (`true`, default) or optional (`false`)
|
|
||||||
- description -- description for documentation of the API
|
|
||||||
|
|
||||||
Another annotation is `@LoggedIn` which takes no arguments. It can be placed before a whole class or before a method, so requests from unauthorized users are forbidden. Permissions can be granted or prohibitted by `@UserIsAllowed` annotation. This one is only per method and takes one argument in `key="value"` format. The value specifies which action (_value_) of a resource (_key_) the user needs to be allowed to perform this request. An example of how an annotated endpoint can look like:
|
|
||||||
|
|
||||||
```{.php}
|
|
||||||
/**
|
|
||||||
* Create a user account
|
|
||||||
* @POST
|
|
||||||
* @LoggedIn
|
|
||||||
* @UserIsAllowed(users="create")
|
|
||||||
* @Param(type="post", name="email", validation="email",
|
|
||||||
* description="An email that will serve as a login name")
|
|
||||||
* @Param(type="post", name="name", validation="string:2..",
|
|
||||||
* description="First name")
|
|
||||||
* @Param(type="post", name="password", validation="string:1..",
|
|
||||||
* msg="Password cannot be empty.",
|
|
||||||
* description="A password for authentication")
|
|
||||||
*/
|
|
||||||
public function actionCreateAccount() {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
The [Doctrine](https://github.com/Kdyby/Doctrine)
|
|
||||||
[ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) framework is used
|
|
||||||
as an object persistence layer. It provides simple to use annotations to specify
|
|
||||||
columns of database tables including types, indexes and also it is possible to
|
|
||||||
make mapping between entities. For detailed info refer to [official
|
|
||||||
documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/).
|
|
||||||
|
|
||||||
The API is capable of sending email messages. They can inform an administrator about errors and users about submission evaluation or a temporary link to change forgotten password. The [Nette Mail](https://doc.nette.org/en/2.4/mailing) extension provides nice interface for sending messages through external SMTP server (**preferred**) or builtin PHP function `mail`. It is important to set up the mailserver properly to ensure message delivery to the clients. The messages are rendered in HTML format via simple _Latte_ templates.
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
|
|
||||||
Instead of relying on PHP sessions, we decided to use an authentication flow
|
|
||||||
based on JWT tokens (RFC 7519). On successful login, the user is issued an
|
|
||||||
access token that they have to send with subsequent requests using the HTTP
|
|
||||||
Authorization header (`Authorization: Bearer <token>`). The token has a limited
|
|
||||||
validity period and has to be renewed periodically using a dedicated API
|
|
||||||
endpoint.
|
|
||||||
|
|
||||||
To implement this behavior in Nette framework, a new `IUserStorage`
|
|
||||||
implementation was created, along with an `IIdentity` and authenticators for
|
|
||||||
both our internal login service and CAS.
|
|
||||||
|
|
||||||
An advantage of this approach is being able control the authentication process
|
|
||||||
completely instead of just receiving session data through a global variable.
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
|||||||
# Web application
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
The *WebApp* is the default user interface of the ReCodEx project. It is a Single Page Application (SPA) which users access through their web browsers.
|
|
||||||
This user interface allows students to join *groups* corresponding to their school classes, submit new *solutions* and see the results as soon as the solution is evaluated by the backend.
|
|
||||||
Teachers can manage their *groups* by adding and removing *students*, assigning *exercises* and examining the *automatically evaluated solutions* of their students and their total score gained throughout the semester.
|
|
||||||
|
|
||||||
The actions performed by the user are sent to the *API server* over the HTTPS protocol and the web application does not modify the database or access files on their server directly and all the inputs sent
|
|
||||||
from the web application are additionally *validated* on the *API server*.
|
|
||||||
|
|
||||||
The *WebApp* is written in *ECMAScript* (often referred to as JavaScript) and HTML5. The [React](https://facebook.github.io/react) library developed by Facebook is used to manage the user interface and the [Redux](http://redux.js.org/) framework
|
|
||||||
created by Dan Abramov is used to manage the application state. Both of these frameworks are industry standards used in production by many companies worldwide. Many other smaller libraries and components are used, notably the [Immutable.js](https://facebook.github.io/immutable-js) and [Format.js](http://formatjs.io/) by Yahoo via *redact-intl* integration library.
|
|
||||||
|
|
||||||
### Multiplatform approach
|
|
||||||
|
|
||||||
Since the runtime environment of *WebApp* is the user's web browser it does not need any installation on the enduser's device and does not require any particular platform or non-standard software installed on the user's device.
|
|
||||||
This application is designed and implemented in a way which should be suitable for modern web browsers on both desktop and mobile devices.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The whole project is written using the next generation of JavaScript referred to as *ECMAScript 6* (also known as *ES6*, *ES.next*, or *Harmony*). Since not all the features introduced in this standard are implemented in today's modern web browsers (like classes and spread operators) and hardly any are implemented in older versions of web browsers which are currently still in use, the source code is transpiled into the older standard *ES5* using [Babel.js](https://babeljs.io/) transpiler and bundled into a single script file using the [webpack](https://webpack.github.io/) moudle bundler. The need for a transpiler also arises from the usage of the *JSX* syntax for declaring React components. To read more about these these tools and their usage please refer to the [installation section](#Installation). The whole bundling process takes place at deployment and is not repeated afterwards.
|
|
||||||
|
|
||||||
### Routing and server side rendering
|
|
||||||
|
|
||||||
The routing mechanism uses the [react-router](https://github.com/ReactTraining/react-router) and [react-router-redux](https://github.com/reactjs/react-router-redux) libraries. The routing mechanism works both on the server and the client.
|
|
||||||
|
|
||||||
The routes are defined in the `/src/pages/routes.js` file.
|
|
||||||
|
|
||||||
### Server rendering and the 'loadAsync' convention
|
|
||||||
|
|
||||||
The server matches the URL of the request with the router and pre-renders the contents of the page. The components, which are selected by the router based on the URL, are given the parameters and are attached to the component tree.
|
|
||||||
|
|
||||||
The server script examines all the components matched with the route and looks for a static function called `loadAsync` and if it is found, it will be executed with the params extracted from the URL, the store's dispatch method and the ID of the logged in user if he is logged in. It is expected that this function will return an instance of `Promise`. Server then waits until all the promises are resolved and only after then it goes on and sends a HTTP response to the client.
|
|
||||||
|
|
||||||
### Client
|
|
||||||
|
|
||||||
Each time the user clicks on a link to a different location of the application the page is not reloaded but the router rather changes the location of the browser to the new URL and replaces the components in the component tree with the ones matched against the new URL the same way the server did. Components should call the `loadAsync` methods themselves in their lifecycle methods.
|
|
||||||
|
|
||||||
### Redux middleware and helpers
|
|
||||||
|
|
||||||
The redux [store](https://github.com/ReCodEx/web-app/blob/master/src/redux/store.js) is extended with several middlewares - [redux-thunk](https://github.com/gaearon/redux-thunk), [redux-promise-middleware](https://github.com/pburtchaell/redux-promise-middleware), router middleware, accessTokenMiddleware, and apiMiddleware.
|
|
||||||
|
|
||||||
#### apiMiddleware
|
|
||||||
|
|
||||||
The API middleware catches all the `CALL_API` actions and turns them into a HTTP request via the [fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API). The middleware deeply converts data in the *body* part of the request to `FormData` and sets correctly the headers and creates endpoints for the API server before each request is sent. The middleware also handle the responses - converts them to the format expected by the reducers and catches requests' failures.
|
|
||||||
|
|
||||||
#### accessTokenMiddleware
|
|
||||||
|
|
||||||
[Access token middleware](https://github.com/ReCodEx/web-app/blob/master/src/redux/middleware/accessTokenMiddleware.js) stores and keeps the access token once the user authenticates and receives a token from the API server. The middleware listens for dispatched API actions and inserts the token to the request automatically.
|
|
||||||
|
|
||||||
#### Modules
|
|
||||||
|
|
||||||
Modules (sometimes called "ducks") contain a reducer and related action types and action creators. There are many separate reducers and actions for each individual resource and task to make the store tree as flat as possible. Vast majority of the modules is derived from the *resource manager* and are very similar to each other. Modules are placed in the `/src/redux/modules` directory.
|
|
||||||
|
|
||||||
#### Selectors
|
|
||||||
|
|
||||||
Selectors are used from the *containers* to return a specific information from the global redux state. It would be very inconvenient if the containers needed to know the whole structure of the state, so they can rely on the functions provided by the selectors for each module. The [reselect](https://github.com/reactjs/reselect) library is used for creating efficient selectors. Selectors are placed in the `/src/redux/selectors` directory.
|
|
||||||
|
|
||||||
#### Resource manager
|
|
||||||
|
|
||||||
The [resource manager](https://github.com/ReCodEx/web-app/blob/master/src/redux/helpers/resourceManager/index.js) is a set of factory functions which was created to extract the repeating functionality of obtaining and managing the loading phases of a resource or multiple resources at once.
|
|
||||||
|
|
||||||
The factory methods create the reducer, action types, and action creators. It strongly relies on the *redux-promise-middleware*. It was inspired by the [Reddit API example](http://redux.js.org/docs/advanced/ExampleRedditAPI.html) from the Redux library official website.
|
|
||||||
|
|
||||||
Resource manager is placed in the `/src/redux/helpers/resourceManager` directory.
|
|
||||||
|
|
||||||
### Declarative UI components
|
|
||||||
|
|
||||||
The implementation of *WebApp* is split across more than a hundred small components which are composed together. There are two basic types of React components - presentational and stateful components.
|
|
||||||
|
|
||||||
- **Presentational components** are in fact pure functions which yield their output based solely on the input *props* and application's *context* (e. g. current language has effect on URLs). These components are preferred and should be as simple as possible. These components are placed in the subfolders of `src/components` directory.
|
|
||||||
|
|
||||||
- **Stateful components** are *connected* to the *redux store* and yield information based on the either the *props* and *context*, but also on the current state of the application. These components are more complicated and often make use of React's components' life cycle methods. *Stateful components* are often referred to as *containers* and the source code files are placed in a separate directories called `src/containers` and `src/pages`.
|
|
||||||
|
|
||||||
- There is no technical difference between *containers* and *pages*. *Pages* are used as "root" components for different pages and are registered in the app's [router](https://github.com/ReactTraining/react-router).
|
|
||||||
|
|
||||||
As was mentioned earlier in the text, there is over a hundred components in the whole application and most of them are very simple. Some of the most important and widely used components are described in the following paragraphs.
|
|
||||||
|
|
||||||
#### App
|
|
||||||
|
|
||||||
[App](https://github.com/ReCodEx/web-app/blob/master/src/containers/App/App.js) container is meant to be the root of the *react-router* tree. It checks whether a user is logged in and loads his profile and settings from the API. App container also handles access token refreshing.
|
|
||||||
|
|
||||||
#### LayoutContainer
|
|
||||||
|
|
||||||
[LayoutContainer](https://github.com/ReCodEx/web-app/blob/master/src/containers/LayoutContainer/LayoutContainer.js) handles the dependency injection of the localized links based on the current language stated in the URL. It also controls the state of the sidebar - collapsing and showing the sidebar.
|
|
||||||
|
|
||||||
##### Layout, Header, Sidebar, Footer
|
|
||||||
|
|
||||||
The [Layout](https://github.com/ReCodEx/web-app/blob/master/src/components/Layout/Layout.js), [Header](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Header/Header.js), [Header](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Footer/Footer.js), and [Sidebar](https://github.com/ReCodEx/web-app/blob/master/src/components/Sidebar/Sidebar/Sidebar.js) components hold the main HTML structure of the page which is displayed to the user and hold links to different parts of the application.
|
|
||||||
|
|
||||||
#### PageContent
|
|
||||||
|
|
||||||
The [PageContent](https://github.com/ReCodEx/web-app/blob/master/src/components/PageContent/PageContent.js) component holds the main content of a page with the common structure for all pages - the *title*, *description*, *[breadcrumbs](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Breadcrumbs/Breadcrumbs.js),*, *content*. The component passes the title and description to the [Helmet](https://github.com/nfl/react-helmet) library which reflects these into the `<head>` section of the HTML document.
|
|
||||||
|
|
||||||
#### ResourceRenderer
|
|
||||||
|
|
||||||
[ResourceRenderer](https://github.com/ReCodEx/web-app/blob/master/src/components/ResourceRenderer/ResourceRenderer.js) component is given a *resource* managed by the *resourceManager* as a *prop* and displays different content based on the state of the given *resource* - still loading, loading failed, fully loaded.
|
|
||||||
|
|
||||||
Passing content for the *loading* and *failed* states though *props* is optional; however, the content for the *loaded* state is required and must be passed as a child to the `ResourceManager`.
|
|
||||||
|
|
||||||
Multiple resources can be passed as an array to the component and it will wait in the *loading* state until some of the resources are still loading. If one of the resources fails to load the component will switch to the *failed* state. When all the files are fully loaded then the component displays the content for the *loaded* state.
|
|
||||||
|
|
||||||
It is worth mentioning that the component is completely stateless and "switching of the states" is the effect of several renderings of the component over time.
|
|
||||||
|
|
||||||
#### Page
|
|
||||||
|
|
||||||
[Page](https://github.com/ReCodEx/web-app/blob/master/src/components/Page/Page.js) combines the two previously described components, since they are often used together for displaying a page content which is dependant on a resource.
|
|
||||||
|
|
||||||
#### Pages
|
|
||||||
|
|
||||||
The main pages of the application are placed in the `/src/pages/` directory. These pages are referenced from *the router* and are responsible mainly for initiating loading of the main resources when the component will be mounted to the DOM.
|
|
||||||
|
|
||||||
#### EvaluationProgressContainer
|
|
||||||
|
|
||||||
#### CommentThreadContainer
|
|
||||||
|
|
||||||
[CommentThreadContainer](https://github.com/ReCodEx/web-app/blob/master/src/containers/CommentThreadContainer/CommentThreadContainer.js) can be used on any page of the *webapp* to create a discussion thread. A unique identification must be assigned to every thread so the comments in the distinct threads do not interleave.
|
|
||||||
|
|
||||||
#### Bootstrap and AdminLTE theme
|
|
||||||
|
|
||||||
The UI of the application is built using the stylesheets from the [Bootstrap framework](http://getbootstrap.com/) from Twitter and the [AdminLTE](https://almsaeedstudio.com/) theme from Abdullah Almsaeed. None of the JavaScript plugins from these libraries is used.
|
|
||||||
|
|
||||||
A package [react-bootstrap](https://react-bootstrap.github.io/) is used. Components specific to the *AdminLTE* theme are implemented and stored in the `src/components/AdminLTE` folder and its subfolders.
|
|
||||||
|
|
||||||
##### Box, FormBox
|
|
||||||
|
|
||||||
[Box](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Box/Box.js) is a frequently used component for bounding other components like text paragraphs or tables inside. It is in fact a re-styled `Panel` component from Bootstrap. It can be collapsable and can be displayed in different colors and types. [FormBox](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/FormBox/FormBox.js) is just a wrapper of `Box` adjusted for holding forms inside of the body.
|
|
||||||
|
|
||||||
##### CommentThread
|
|
||||||
|
|
||||||
[CommentThread](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/CommentThread/CommentThread.js) component is used directly by the earlier described `CommentThreadContainer` and uses the HTML markup introduced by the theme author to achieve nice comment threads.
|
|
||||||
|
|
||||||
##### Avatar, LoadingAvatar, FailedAvatar
|
|
||||||
|
|
||||||
[Avatar](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Avatar/Avatar.js) component is used on many places for displaying a round profile picture of a user. `LoadingAvatar` and `FailedAvatar` are used to mock the visual appearance of the avatar while the image is being downloaded or if the download failed for some reason.
|
|
||||||
|
|
||||||
#### Icons
|
|
||||||
|
|
||||||
The free [FontAwesome](http://fontawesome.io/) icon pack is used in the application via the [react-fontawesome](https://github.com/danawoodman/react-fontawesome) library. Many types of [icons](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Header/Header.js) are defined as components (e. g. `LoadingIcon`, `WarningIcon`, `SuccessIcon`, `DeleteIcon`, and many others) to be used throughout the application so the same symbols are used for the same purposes.
|
|
||||||
|
|
||||||
#### Forms
|
|
||||||
|
|
||||||
The [redux-form](http://redux-form.com/) library is used for the management of forms' states. Several of the implemented components can be used as form fields, namely the [CheckboxField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/CheckboxField.js), [MarkdownTextAreaField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/MarkdownTextAreaField.js), [SourceCodeField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/SourceCodeField.js), [DatetimeField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/DatetimeField.js), [TabbedArrayField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/TabbedArrayField.js), [LanguageSelectField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/LanguageSelectField.js), and a few others.
|
|
||||||
|
|
||||||
### Localization and globalization
|
|
||||||
|
|
||||||
The whole application is prepared for localization. All the text can be exported from the user interface and translated into several languages. The numbers, dates, and time values are also formatted with respect to the selected language. The [react-intl](https://github.com/yahoo/react-intl) and [Moment.js](http://momentjs.com/) libraries are used to achieve this.
|
|
||||||
|
|
||||||
All the strings can be extracted from the application using a command:
|
|
||||||
```
|
|
||||||
$ npm run exportStrings
|
|
||||||
```
|
|
||||||
This will create *JSON* files with the exported strings for the *'en'* and *'cs'* locale. If you want to export strings for more languages, you must edit the `/manageTranslations.js` script. The exported strings are placed in the `/src/locales` directory.
|
|
||||||
|
|
Loading…
Reference in New Issue