Merge branch 'master' into production
This commit is contained in:
commit
7df7386a97
16 changed files with 280 additions and 15 deletions
|
@ -1,5 +1,14 @@
|
||||||
FROM golang:latest
|
############################
|
||||||
|
# STEP 1 build executable binary
|
||||||
|
############################
|
||||||
|
|
||||||
|
FROM golang:alpine AS builder
|
||||||
|
|
||||||
|
# Install git.
|
||||||
|
# Git is required for fetching the dependencies.
|
||||||
|
|
||||||
|
RUN apk update && apk add --no-cache git
|
||||||
|
|
||||||
ENV PATH=$PATH:/src/oef/
|
ENV PATH=$PATH:/src/oef/
|
||||||
RUN mkdir -p /src/oef
|
RUN mkdir -p /src/oef
|
||||||
ADD . /src/oef
|
ADD . /src/oef
|
||||||
|
@ -8,7 +17,48 @@ COPY config/config.aws.yaml /src/oef/config/config.yaml
|
||||||
|
|
||||||
WORKDIR /src/oef/
|
WORKDIR /src/oef/
|
||||||
|
|
||||||
RUN go build -o oef *.go
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o oef *.go
|
||||||
|
# RUN go build -o oef *.go
|
||||||
|
|
||||||
|
############################
|
||||||
|
# STEP 2 build a small image
|
||||||
|
############################
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
# Copy our static executable.
|
||||||
|
COPY --from=builder /src/oef/oef /src/oef/oef
|
||||||
|
|
||||||
|
# Copy config file
|
||||||
|
COPY --from=builder /src/oef/config/config.yaml /src/oef/config/config.yaml
|
||||||
|
|
||||||
|
# Copy templates
|
||||||
|
COPY --from=builder /src/oef/templates /src/oef/templates
|
||||||
|
|
||||||
|
# Copy dist
|
||||||
|
COPY --from=builder /src/oef/dist /src/oef/dist
|
||||||
|
|
||||||
|
# Copy VERSION file
|
||||||
|
COPY --from=builder /src/oef/VERSION /src/oef/VERSION
|
||||||
|
|
||||||
|
WORKDIR /src/oef/
|
||||||
|
|
||||||
|
# Run the oef binary.
|
||||||
ENTRYPOINT ["/src/oef/oef"]
|
ENTRYPOINT ["/src/oef/oef"]
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
|
# FROM golang:latest
|
||||||
|
|
||||||
|
# ENV PATH=$PATH:/src/oef/
|
||||||
|
# RUN mkdir -p /src/oef
|
||||||
|
# ADD . /src/oef
|
||||||
|
|
||||||
|
# COPY config/config.aws.yaml /src/oef/config/config.yaml
|
||||||
|
|
||||||
|
# WORKDIR /src/oef/
|
||||||
|
|
||||||
|
# RUN go build -o oef *.go
|
||||||
|
# ENTRYPOINT ["/src/oef/oef"]
|
||||||
|
|
||||||
|
# EXPOSE 80
|
||||||
|
|
23
dist/main.bundle.js
vendored
23
dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
10
docker/oef_aws/db.env
Normal file
10
docker/oef_aws/db.env
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
MYSQL_ROOT_PASSWORD=oef
|
||||||
|
MYSQL_PASSWORD=oef
|
||||||
|
MYSQL_DATABASE=oef_prod
|
||||||
|
MYSQL_USER=oef
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
14
docker/oef_aws/docker-compose.yml
Normal file
14
docker/oef_aws/docker-compose.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
version: "3.3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
app:
|
||||||
|
image: andrea/oef:latest
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
environment:
|
||||||
|
- DB_HOST=oef-micro.cjsemjfwbreb.eu-central-1.rds.amazonaws.com
|
||||||
|
- DB_PORT=3306
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ services:
|
||||||
- "4444:4444"
|
- "4444:4444"
|
||||||
- "5900:5900"
|
- "5900:5900"
|
||||||
volumes:
|
volumes:
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- /dev/shm:/dev/shm
|
- /dev/shm:/dev/shm
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
@ -240,14 +240,25 @@ func (model *Response) Update(db *Database, args map[string]string, w http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the contest is still active
|
// Check if the contest is still active
|
||||||
if !response.(*Response).IsActive() {
|
if isParticipant(r) && !response.(*Response).IsActive() {
|
||||||
return nil, errors.OutOfTime
|
return nil, errors.OutOfTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.(*Response).SingleResponses = make([]*SingleResponse, 0)
|
||||||
|
|
||||||
err = renderer.Decode(response, r)
|
err = renderer.Decode(response, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there aren't single responses then participant
|
||||||
|
// response Starttime is resetted.
|
||||||
|
if isAdministrator(r) {
|
||||||
|
if len(response.(*Response).SingleResponses) == 0 {
|
||||||
|
response.(*Response).StartTime = time.Time{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WriteUpdater(r, response.(*Response))
|
WriteUpdater(r, response.(*Response))
|
||||||
|
|
||||||
_, err = SaveResponse(db, response)
|
_, err = SaveResponse(db, response)
|
||||||
|
|
|
@ -231,6 +231,106 @@ func (t *testSuite) TestSchoolSubscription() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testSuite) TestParticipantResponse() {
|
||||||
|
login("WINBOXGUF", "GUf3wIP0DF7")
|
||||||
|
|
||||||
|
err := findElement("a.btn-primary").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("#update-response").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
correct := []string{
|
||||||
|
"97", "129", "105", "113", "93", "89", "109", "141", "157",
|
||||||
|
"85", "133", "153", "121", "117", "101", "149", "81", "145",
|
||||||
|
"125", "137",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, answerId := range correct {
|
||||||
|
err = findElement("#answer_" + answerId).Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("button.btn-primary").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logout()
|
||||||
|
|
||||||
|
login("admin", "admin")
|
||||||
|
|
||||||
|
err = findElement("#responses_navbar_link").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("#myInput").SendKeys("BOXILL")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = findElement("a.oef-selected-by-search").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreEl := findElement("#score")
|
||||||
|
score, err := scoreEl.Text()
|
||||||
|
|
||||||
|
t.Nil(err)
|
||||||
|
t.Equal("20", score)
|
||||||
|
|
||||||
|
err = findElement("a.btn-primary").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("#reset_responses").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("button.btn-primary").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logout()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSuite) TestContestActiveWindow() {
|
||||||
|
login("admin", "admin")
|
||||||
|
|
||||||
|
err := searchAndClick("junior")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("a.btn-primary").Click()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = findElement("#contest_description").SendKeys("Gara per la categoria JUNIOR")
|
||||||
|
|
||||||
|
err = findElement("#contest_date").SendKeys("2020-03-04")
|
||||||
|
err = findElement("#contest_start_time").SendKeys("14:00")
|
||||||
|
err = findElement("#contest_end_time").SendKeys("15:00")
|
||||||
|
err = findElement("#contest_duration").SendKeys("45")
|
||||||
|
|
||||||
|
err = findElement(".btn-primary").Click()
|
||||||
|
|
||||||
|
t.True(true)
|
||||||
|
|
||||||
|
logout()
|
||||||
|
}
|
||||||
|
|
||||||
func findElement(selector string) selenium.WebElement {
|
func findElement(selector string) selenium.WebElement {
|
||||||
element, err := wd.FindElement(selenium.ByCSSSelector, selector)
|
element, err := wd.FindElement(selenium.ByCSSSelector, selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -436,3 +536,16 @@ func clickPrimaryButton() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchAndClick(text string) error {
|
||||||
|
err := findElement("#myInput").SendKeys(text)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = findElement("a.oef-selected-by-search").Click()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
1
scripts/export_schools/.gitignore
vendored
Normal file
1
scripts/export_schools/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.csv
|
BIN
scripts/export_schools/export_schools
Executable file
BIN
scripts/export_schools/export_schools
Executable file
Binary file not shown.
36
scripts/export_schools/main.go
Normal file
36
scripts/export_schools/main.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.andreafazzi.eu/andrea/oef/client"
|
||||||
|
"git.andreafazzi.eu/andrea/oef/orm"
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
username := flag.String("username", "admin", "Username")
|
||||||
|
password := flag.String("password", "admin", "Password")
|
||||||
|
output := flag.String("output", "schools.csv", "Output filename")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
client, err := client.Dial(flag.Arg(0), *username, *password)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
schools := make([]*orm.School, 0)
|
||||||
|
err = client.ReadAll(&schools)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(*output)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
gocsv.MarshalFile(schools, f)
|
||||||
|
}
|
BIN
scripts/export_schools/scuole.xlsx
Normal file
BIN
scripts/export_schools/scuole.xlsx
Normal file
Binary file not shown.
14
src/index.js
14
src/index.js
|
@ -2,7 +2,7 @@ import './style.css'
|
||||||
|
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
|
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
var timeleft = parseInt($("#timeleft").html());
|
var timeleft = parseInt($("#timeleft").html());
|
||||||
if (timeleft > 0) {
|
if (timeleft > 0) {
|
||||||
|
@ -10,7 +10,13 @@ $(function () {
|
||||||
} else {
|
} else {
|
||||||
timeleft = 0;
|
timeleft = 0;
|
||||||
}
|
}
|
||||||
$("#timeleft").html(timeleft)
|
|
||||||
|
$("#timeleft").html(timeleft);
|
||||||
|
|
||||||
|
var minutes = Math.floor(timeleft/60);
|
||||||
|
var seconds = timeleft % 60;
|
||||||
|
|
||||||
|
$("#timeleft_view").html(minutes+":"+seconds.toString().padStart(2, "0"));
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
$("#reloadCaptcha").on("click",function(eventObject) {
|
$("#reloadCaptcha").on("click",function(eventObject) {
|
||||||
|
@ -66,4 +72,8 @@ $(function () {
|
||||||
|
|
||||||
$(".karmen-ajax-delete").on("click", deleteHandler);
|
$(".karmen-ajax-delete").on("click", deleteHandler);
|
||||||
|
|
||||||
|
$("#reset_responses").on("click", function(eventObject) {
|
||||||
|
$('.form-check-input').removeAttr('checked');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answer"}}">Risposte</a>
|
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answer"}}">Risposte</a>
|
||||||
<a id="schools_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{all "School"}}">Scuole</a>
|
<a id="schools_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{all "School"}}">Scuole</a>
|
||||||
<a id="participants_navbar_link" class="nav-item nav-link {{.Options|active "Participant"}}" href="{{all "Participant"}}">Partecipanti</a>
|
<a id="participants_navbar_link" class="nav-item nav-link {{.Options|active "Participant"}}" href="{{all "Participant"}}">Partecipanti</a>
|
||||||
<a class="nav-item nav-link {{.Options|active "Response"}}" href="{{all "Response"}}">Prove</a>
|
<a id="responses_navbar_link" class="nav-item nav-link {{.Options|active "Response"}}" href="{{all "Response"}}">Prove</a>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- if $isSchool -}}
|
{{- if $isSchool -}}
|
||||||
<a id="school_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{.Claims|modelId|show "School"}}">Scuola</a>
|
<a id="school_navbar_link" class="nav-item nav-link {{.Options|active "School"}}" href="{{.Claims|modelId|show "School"}}">Scuola</a>
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
<footer class="footer text-center">
|
<footer class="footer text-center">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="text-center text-muted">© 2020 <a href="https://www.olimpiadi-economiaefinanza.it">Olimpiadi di Economia e Finanza 2020</a> ({{version}})
|
<span class="text-center text-muted">© 2020 <a href="https://www.olimpiadi-economiaefinanza.it">Olimpiadi di Economia e Finanza</a> ({{version}})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
{{if isResponseIn $answer.ID $.Data.AnswersIDs}}
|
{{if isResponseIn $answer.ID $.Data.AnswersIDs}}
|
||||||
{{$checked = true}}
|
{{$checked = true}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" required {{if $checked}}checked{{end}}>
|
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" {{if $isParticipant}}required{{end}} {{if $checked}}checked{{end}}>
|
||||||
<label class="form-check-label {{if and $isAdmin $answer.Correct}}text-success{{end}}" for="answer_{{$answer.ID}}">
|
<label class="form-check-label {{if and $isAdmin $answer.Correct}}text-success{{end}}" for="answer_{{$answer.ID}}">
|
||||||
{{$answer}}
|
{{$answer}}
|
||||||
</label>
|
</label>
|
||||||
|
@ -71,10 +71,12 @@
|
||||||
<dt>Gara</dt><dd>{{.Data.Contest}}</dd>
|
<dt>Gara</dt><dd>{{.Data.Contest}}</dd>
|
||||||
<dt>Partecipante</dt><dd>{{.Data.Participant}}</dd>
|
<dt>Partecipante</dt><dd>{{.Data.Participant}}</dd>
|
||||||
{{- if $isParticipant -}}
|
{{- if $isParticipant -}}
|
||||||
<dt>Tempo rimanente</dt>{{if .Data.TimeLeft}}<dd><span id="timeleft">{{.Data.TimeLeft.Seconds|toInt}}</span> secondi rimanenti</dd>{{else}}<dd>La gara è sempre attiva</dd>{{end}}
|
<span id="timeleft" style="display: none">{{.Data.TimeLeft.Seconds|toInt}}</span>
|
||||||
|
<dt>Tempo rimanente</dt>{{if .Data.TimeLeft}}<dd><span id="timeleft_view"></span> minuti rimanenti</dd>{{else}}<dd>La gara è sempre attiva</dd>{{end}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</dl>
|
</dl>
|
||||||
<button type="submit" class="btn btn-primary" form="{{$form}}">Salva</button>
|
<button type="submit" class="btn btn-primary" form="{{$form}}">Salva</button>
|
||||||
|
{{if $isAdmin}}<button id="reset_responses" class="btn btn-outline-secondary">Cancella risposte</button>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<dd class="col-sm-9">nessuna risposta fornita</dd>
|
<dd class="col-sm-9">nessuna risposta fornita</dd>
|
||||||
{{end}}
|
{{end}}
|
||||||
<dt class="col-sm-3">Punteggio</dt>
|
<dt class="col-sm-3">Punteggio</dt>
|
||||||
<dd class="col-sm-9">{{.Data.Score}}</dd>
|
<dd id="score" class="col-sm-9">{{.Data.Score}}</dd>
|
||||||
{{if $creatorUser:=.Data.CreatedBy}}
|
{{if $creatorUser:=.Data.CreatedBy}}
|
||||||
<dt class="col-sm-3">Creato da</dt>
|
<dt class="col-sm-3">Creato da</dt>
|
||||||
<dd class="col-sm-9">{{$creatorUser.Username}}[{{$creatorUser.Role}}] {{$.Data.CreatedAt|prettyDateTime}} da {{.Data.CreatorIP}}</dd>
|
<dd class="col-sm-9">{{$creatorUser.Username}}[{{$creatorUser.Role}}] {{$.Data.CreatedAt|prettyDateTime}} da {{.Data.CreatorIP}}</dd>
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
{{if .Data.IsActive}}
|
{{if .Data.IsActive}}
|
||||||
<p>
|
<p>
|
||||||
Per iniziare o riprendere la gara clicca sul pulsante a destra.
|
Per iniziare o riprendere la gara clicca sul pulsante a destra.
|
||||||
<a id="new_participant_subscribe" href="{{.Data.ID | update "Response"}}" class="float-right btn btn-primary btn-sm">
|
<a id="update-response" href="{{.Data.ID | update "Response"}}" class="float-right btn btn-primary btn-sm">
|
||||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||||
Vai alla gara!
|
Vai alla gara!
|
||||||
</a>
|
</a>
|
||||||
|
|
Loading…
Reference in a new issue